Initial commit

This commit is contained in:
2023-09-25 20:27:38 +08:00
committed by GitHub
commit 283dba9fe1
21 changed files with 957 additions and 0 deletions

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
BasedOnStyle: Google
DerivePointerAlignment: false
PointerAlignment: Right
ColumnLimit: 120

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.cache/
.vscode/
build/
src/std/

5
CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(Minesweeper)
add_subdirectory(src)

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 ACMClassCourse-2023
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

187
README.md Normal file
View File

@ -0,0 +1,187 @@
# Minesweeper-2023
> ACM 班 2023 级程序设计第一次大作业
~~大作业~~ 扫雷,启动!
## 目录
1. [Minesweeper-2023](#minesweeper-2023)
1. [目录](#目录)
2. [简介](#简介)
1. [背景](#背景)
2. [作业目标](#作业目标)
3. [作业要求](#作业要求)
1. [术语解释](#术语解释)
2. [作业任务](#作业任务)
1. [基础任务](#基础任务)
2. [进阶任务](#进阶任务)
3. [数据范围](#数据范围)
4. [须知](#须知)
1. [截止时间](#截止时间)
2. [评分规则](#评分规则)
1. [A班](#a班)
2. [B班](#b班)
## 简介
### 背景
Minesweeper扫雷是我们熟悉的经典轻量级小游戏作为老版本 Windows 操作系统的内置应用,它在许多人的回忆中可能是学生时代在老电脑里的娱乐,信息课上的摸鱼或是竞赛中垃圾时间的消遣。
![微软扫雷](figures/microsoft-minesweeper.png)
自从 Windows 8 从内置游戏中移除了扫雷,关于扫雷的回忆可能已经随我们的那段童年一起被慢慢地尘封。那么在这里,在你初入 ACM 班的第一个大作业中,让我们来回顾一下扫雷吧~
### 作业目标
我们想通过本次作业达到以下目标:
1. 基础任务
- 熟悉基本的输入输出与控制流语句
- 熟悉函数的使用
- 熟悉数组的使用
- 熟悉函数的递归
- 了解搜索算法(可选)
- 边界情况的处理
- 规范代码风格
2. 进阶任务
- 提高模拟水平
- 运用算法解决实际问题的能力
- 使用 git 与 cmake 管理项目(可选)
- 运用机器学习解决问题(可选)
## 作业要求
在本次作业中,你需要实现一个扫雷游戏(如果你不知道扫雷是什么,你可以参考 [扫雷_百度百科](https://baike.baidu.com/item/扫雷/12543) )。
### 术语解释
由于本次作业涉及到一些为了方便表述而使用的术语,我们会先进行解释
- 地图,格子:在上面的游戏截图中,每一个小方块被我们称为一个格子,所有的格子构成地图
- 地雷,非地雷格子,非地雷格子的地雷计数:游戏中的格子可能是地雷或非地雷格子;若一个格子是非地雷格子,则我们将其周围八个格子中地雷的总数称为其地雷计数。
- 访问一个格子:在游戏最开始,所有格子都是**未被访问**的(即其内容对于用户是未知的),用户访问一个格子相当于传统扫雷游戏中左键单击该格子,其状态将变为已访问。
- 需要注意的是,与实际的扫雷游戏相同,**当你访问某个地雷计数为 0 的格子时,周围的所有格子将自动被访问**(相应地,若自动被访问的格子中也有地雷计数为 0 的,该过程需继续进行下去;对于 B 班同学,你可能需要运用递归的思想解决这一问题,如果你对这一任务的实现没有头绪,请咨询助教)
- 游戏结束条件:若一个格子是地雷,则访问该格子将**立即**导致游戏失败,否则游戏继续,直到所有非地雷的格子均被访问(即游戏胜利)。
### 作业任务
#### 基础任务
在本次作业中,你的基础任务是模拟实现一个命令行交互的扫雷小游戏,即实现该游戏的服务端。
首先,你会通过标准输入得到给定地图的信息(行列数量与每个格子是否为地雷),形式化地,第一行输入两个整数 `n` `m` ,代表地图有 `n``m` 列,随后 `n` 行,每行输入 `m` 个字符(不计换行符),以换行符结尾,其中第 `i` 行第 `j`0-based下同代表地图中第 `i` 行第 `j` 个格子的内容,`.` 代表未放置地雷,`X` 代表放置地雷。
例如下面的输入
```
3 3
.X.
...
..X
```
代表当前游戏的地图大小为 3 * 3在地图的第 0 行第 1 列与第 2 行第 2 列放置了地雷。
在初始状态下,地图中的所有格子都是未被访问的。
初始化完成后,你需要输出地图(格式见下)。随后,你会通过标准输入得到若干行字符串来表示玩家的操作,每一行有两个正整数 `x y`,分别是两个在 `[0, n - 1]``[0, m - 1]` 范围的数字,代表玩家这一步希望访问第 `x` 行第 `y` 列的格子。
在读取玩家的输入后,你需要访问指定的格子(若格子本身已被访问,不做任何操作);并将访问后的地图状态输出,格式与输入相仿,具体地
- 输出 `n``m` 列,最后输出换行符
- 未被访问的格子输出为 `?`,例如上面的地图在初始状态下的输出应当为
```
???
???
???
```
- 对于已被访问过的格子,若其为地雷,输出 `X`,若其不是地雷,输出一个正整数 `k`,表示其地雷计数。例如对于上面的地图,若从左到右依次访问最下面一行的格子,在右下角的地雷被访问后的输出应当为
```
???
12?
01X
```
(若你不明白为什么 (1, 0) 和 (1, 1) 两个格子也被访问,请参考 [术语解释](#术语解释)
- 当所有非地雷格子已被访问后(即游戏胜利前最后一次输出地图时),你需要将在所有为地雷的格子处(均未访问)输出 `@`,非地雷格子同上。例如,对于上面的地图,游戏胜利前最后一次输出应当为
```
1@1
122
01@
```
- 输出地图后,你需要判定游戏是否结束(失败或胜利),若游戏结束,你应当先输出一行文字,**胜利则为 "YOU WIN!",失败则为 "GAME OVER!"**,再输出一行两个整数 `visit_count step_count`,以空格分隔,分别代表玩家访问过的**非地雷格子**的个数与总操作数,并立即以 0 为返回值退出程序。
- 例如,在上面的访问了右下角地雷的例子中,当你输出完地图后,你应输出下面的信息并退出程序:
```
GAME OVER!
4 3
```
#### 进阶任务
在本次作业中,你的进阶任务是游玩你刚刚设计的扫雷小游戏,即实现该游戏的用户端。这一任务是为 A 班,即参加过算法竞赛的同学准备的,你可以将其类比为算法竞赛中的交互题。有所不同的是,我们这里不使用标准输入输出进行交互,而是通过你刚刚自己写的游戏程序交互。
我们下面将游戏程序称为服务端代码(即 `server.h`),游玩程序称为用户端代码(即 `client.h`)。
首先,我们将读入游戏地图作为服务端代码的输入,用户端代码仅可以获知游戏地图的行数与列数;接下来,用户端代码不断发起 `Execute(row, column)` 指令(第一次将由输入数据代你发出,防止第一次就踩雷),即访问某一个方块并获得该访问后的地图情况,具体地,`Execute(row, column)` 函数的实现形如
```
Execute(row, column):
VisitBlock(row, column)
ReadMap() from result of PrintMap()
```
因此,如你所见,你需要在 ReadMap 函数中读取有限的地图信息,并储存到一些全局变量中,随后根据这些地图信息做出下一步的决策,通过 `Execute` 再次发起决策。具体的代码结构逻辑可以参考 `advanced.cpp` 和 `client.cpp`,如果你不明白代码的实现方式,请立即询问助教!!!
例如,若输入数据为
```
3 3
.X.
...
..X
2 0
```
最下面一行 `2 0` 是输入数据代你做出的第一次决策,下面给出了一组交互的例子。
```
(Decision) (result of PrintMap)
???
12?
01?
1 2
???
122
01?
0 1
?X?
122
01?
GAME OVER!
5 3
```
注意这里的最后两行是游戏结束(失败)的输出,`step_count` 为 3 是因为将第一次操作 `2 0` 计算入中操作数。
### 数据范围
对于基础任务,地图的行数`n`、列数`m`、地雷数`x`满足1 < `n`, `m` < 310 < `x` < mn保证输入合法
## 须知
### 截止时间
第五周周一10/918:00
### 评分规则
A班B班均有最多 10% bonus 分数
#### A班
| 任务 | 占比 |
| :---: | :---: |
| 基础任务公开测试点 | 60% |
| 进阶任务 | 30% |
| Code Review | 20% |
#### B班
| 任务 | 占比 |
| :---: | :---: |
| 基础任务 | 80% |
| 进阶任务 | 10% |
| Code Review | 20% |
关于进阶任务的评分细则我们将稍后发布

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

10
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
set(PROJECT_NAME ${CMAKE_PROJECT_NAME})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-g -O2")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_executable(server main.cpp)
# add_executable(client advanced.cpp) # For advanced task

41
src/advanced.cpp Normal file
View File

@ -0,0 +1,41 @@
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include "client.h"
#include "server.h"
/**
* @brief The implementation of function Execute
* @details Use it only when trying advanced task. Do NOT modify it before discussing with TA.
*/
void Execute(int row, int column) {
std::string str;
VisitBlock(row, column);
if (game_state != 0) {
ExitGame();
}
std::ostringstream oss;
std::streambuf *old_output_buffer = std::cout.rdbuf();
std::cout.rdbuf(oss.rdbuf());
// Here, we redirect the output stream to the string stream.
// By this way the output of PrintMap() would be stored in the string.
// If you do not understand, you can try to compare it with freopen, which redirect the output stream to a file.
PrintMap();
std::cout.rdbuf(old_output_buffer); // Restore the output buffer
str = oss.str(); // Read the output
std::istringstream iss(str); // Redirect the input to the string, which stores the output recently
std::streambuf *old_input_buffer = std::cin.rdbuf();
ReadMap();
std::cin.rdbuf(old_input_buffer);
}
int main() {
InitMap();
std::cout << rows << " " << columns << std::endl;
InitGame();
while (true) {
Decide(); // Exit() will be called in this function
}
}

63
src/include/client.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef CLIENT_H
#define CLIENT_H
#include <iostream>
#include <utility>
extern int rows; // The count of rows of the game map
extern int columns; // The count of columns of the game map
// You can not use any other external variables except for rows and columns.
/**
* @brief The definition of function Execute(int, int)
*
* @details This function is designed to take a step when player the client's (or player's) role, and the implementation
* of it has been finished by TA. (I hope my comments in code would be easy to understand T_T) If you do not understand
* the contents, please ask TA for help immediately!!!
*
* @param row The row coordinate (0-based) of the block to be visited.
* @param column The column coordinate (0-based) of the block to be visited.
*/
void Execute(int row, int column);
/**
* @brief The definition of function InitGame()
*
* @details This function is designed to initialize the game. It should be called at the beginning of the game, which
* will read the scale of the game map and the first step taken by the server (see README).
*/
void InitGame() {
int first_row, first_column;
std::cin >> first_row >> first_column;
Execute(first_row, first_column);
}
/**
* @brief The definition of function ReadMap()
*
* @details This function is designed to read the game map from stdin when playing the client's (or player's) role.
* Since the client (or player) can only get the limited information of the game map, so if there is a 3 * 3 map as
* above and only the block (2, 0) has been visited, the stdin would be
* ???
* 12?
* 01?
*/
void ReadMap() {
// TODO (student): Implement me!
}
/**
* @brief The definition of function Decide()
*
* @details This function is designed to decide the next step when playing the client's (or player's) role. Open up your
* mind and make your decision here!
*/
void Decide() {
// TODO (student): Implement me!
// while (true) {
// Execute(0, 0);
// }
}
#endif

106
src/include/server.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef SERVER_H
#define SERVER_H
#include <cstdlib>
#include <iostream>
/*
* You may need to define some global variables for the information of the game map here.
* Although we don't encourage to uss global variables in real cpp projects, you may have to use them because the use of
* class is not taught yet. However, if you are member of A-class or have learnt the use of cpp class, member functions,
* etc., you're free to modify this structure.
*/
int rows; // The count of rows of the game map
int columns; // The count of columns of the game map
int game_state; // The state of the game, 0 for continuing, 1 for winning, -1 for losing
/**
* @brief The definition of function InitMap()
*
* @details This function is designed to read the initial map from stdin. For example, if there is a 3 * 3 map in which
* mines are located at (0, 1) and (1, 2) (0-based), the stdin would be
* 3 3
* .X.
* ...
* ..X
* where X stands for a mine block and . stands for a normal block. After executing this function, your game map would
* be initialized, with all the blocks unvisited.
*/
void InitMap() {
std::cin >> rows >> columns;
// TODO (student): Implement me!
}
/**
* @brief The definition of function VisitBlock(int, int)
*
* @details This function is designed to visit a block in the game map. We take the 3 * 3 game map above as an example.
* At the beginning, if you call VisitBlock(0, 0), the return value would be 0 (game continues), and the game map would
* be
* 1??
* ???
* ???
* If you call VisitBlock(0, 1) after that, the return value would be -1 (game ends and the players loses) , and the
* game map would be
* 1X?
* ???
* ???
* If you call VisitBlock(0, 2), VisitBlock(2, 0), VisitBlock(1, 2) instead, the return value of the last operation
* would be 1 (game ends and the player wins), and the game map would be
* 1@1
* 122
* 01@
*
* @param row The row coordinate (0-based) of the block to be visited.
* @param column The column coordinate (0-based) of the block to be visited.
*
* @note You should edit the value of game_state in this function. Precisely, edit it to
* 0 if the game continues after visit that block, or that block has already been visited before.
* 1 if the game ends and the player wins.
* -1 if the game ends and the player loses.
*/
void VisitBlock(int row, int column) {
// TODO (student): Implement me!
}
/**
* @brief The definition of function PrintMap()
*
* @details This function is designed to print the game map to stdout. We take the 3 * 3 game map above as an example.
* At the beginning, if you call PrintMap(), the stdout would be
* ???
* ???
* ???
* If you call VisitBlock(2, 0) and PrintMap() after that, the stdout would be
* ???
* 12?
* 01?
* If you call VisitBlock(0, 1) and PrintMap() after that, the stdout would be
* ?X?
* 12?
* 01?
* If the player visits all blocks without mine and call PrintMap() after that, the stdout would be
* 1@1
* 122
* 01@
* (You may find the global variable game_state useful when implementing this function.)
*
* @note Use std::cout to print the game map, especially when you want to try the advanced task!!!
*/
void PrintMap() {
// TODO (student): Implement me!
}
/**
* @brief The definition of function ExitGame()
*
* @details This function is designed to exit the game.
* It outputs a line according to the result, and a line of two integers, visit_count and step_count,
* representing the number of blocks visited and the number of steps taken respectively.
*/
void ExitGame() {
// TODO (student): Implement me!
exit(0); // Exit the game immediately
}
#endif

19
src/main.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <iostream>
#include "server.h"
int main() {
InitMap();
PrintMap();
while (true) {
int pos_x;
int pos_y;
std::cin >> pos_x >> pos_y;
VisitBlock(pos_x, pos_y);
PrintMap();
if (game_state != 0) {
ExitGame();
}
}
return 0;
}

12
testcases/basic/basic1.in Normal file
View File

@ -0,0 +1,12 @@
5 5
..X..
.X.X.
X.X.X
.X.X.
..X..
0 0
0 1
1 2
3 4
4 4
2 2

View File

@ -0,0 +1,37 @@
?????
?????
?????
?????
?????
1????
?????
?????
?????
?????
12???
?????
?????
?????
?????
12???
??4??
?????
?????
?????
12???
??4??
?????
????2
?????
12???
??4??
?????
????2
????1
12???
??4??
??X??
????2
????1
GAME OVER!
5 6

22
testcases/basic/basic2.in Normal file
View File

@ -0,0 +1,22 @@
5 5
...XX
.X..X
..X..
.X.X.
X...X
0 2
0 0
0 1
1 0
1 2
1 3
2 3
2 4
3 4
3 2
2 0
2 1
3 0
4 3
4 2
4 4

View File

@ -0,0 +1,87 @@
?????
?????
?????
?????
?????
??2??
?????
?????
?????
?????
1?2??
?????
?????
?????
?????
112??
?????
?????
?????
?????
112??
1????
?????
?????
?????
112??
1?3??
?????
?????
?????
112??
1?34?
?????
?????
?????
112??
1?34?
???3?
?????
?????
112??
1?34?
???32
?????
?????
112??
1?34?
???32
????2
?????
112??
1?34?
???32
??3?2
?????
112??
1?34?
2??32
??3?2
?????
112??
1?34?
23?32
??3?2
?????
112??
1?34?
23?32
2?3?2
?????
112??
1?34?
23?32
2?3?2
???2?
112??
1?34?
23?32
2?3?2
??22?
112??
1?34?
23?32
2?3?2
??22X
GAME OVER!
15 16

25
testcases/basic/basic3.in Normal file
View File

@ -0,0 +1,25 @@
5 5
....X
XX..X
.X...
..X..
.X..X
4 3
4 3
4 2
4 0
0 0
2 0
2 2
2 3
2 4
0 1
0 2
0 3
1 2
1 3
3 0
3 1
3 3
3 3
3 4

102
testcases/basic/basic3.out Normal file
View File

@ -0,0 +1,102 @@
?????
?????
?????
?????
?????
?????
?????
?????
?????
???2?
?????
?????
?????
?????
???2?
?????
?????
?????
?????
??22?
?????
?????
?????
?????
1?22?
2????
?????
?????
?????
1?22?
2????
?????
3????
?????
1?22?
2????
?????
3?3??
?????
1?22?
2????
?????
3?32?
?????
1?22?
2????
?????
3?321
?????
1?22?
22???
?????
3?321
?????
1?22?
221??
?????
3?321
?????
1?22?
2212?
?????
3?321
?????
1?22?
2212?
??2??
3?321
?????
1?22?
2212?
??22?
3?321
?????
1?22?
2212?
??22?
3?321
2????
1?22?
2212?
??22?
3?321
23???
1?22?
2212?
??22?
3?321
23?2?
1?22?
2212?
??22?
3?321
23?2?
1?22?
2212@
@@22@
3@321
23@21
1@22@
YOU WIN!
17 19

11
testcases/basic/basic4.in Normal file
View File

@ -0,0 +1,11 @@
9 9
.........
.........
..X...X..
.........
.........
.........
..X...X..
.........
.........
4 4

View File

@ -0,0 +1,20 @@
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
000000000
011101110
01@101@10
011101110
000000000
011101110
01@101@10
011101110
000000000
YOU WIN!
77 1

26
testcases/basic/basic5.in Normal file
View File

@ -0,0 +1,26 @@
9 9
.........
...X.....
.....X.XX
.........
..X......
.........
.........
X.....X..
.........
8 8
8 6
8 0
3 1
3 0
2 6
2 4
2 3
1 8
1 7
1 6
1 5
1 4
0 3
0 4
0 5

155
testcases/basic/basic5.out Normal file
View File

@ -0,0 +1,155 @@
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
?????????
???111222
???100000
?11100000
?10001110
?10001?10
?10001?10
?????????
?????????
?????????
???111222
???100000
?11100000
?10001110
?10001?10
?10001110
?????????
?????????
?????????
???111222
???100000
?11100000
?10001110
?10001?10
110001110
?????????
?????????
?????????
?1?111222
???100000
?11100000
?10001110
?10001?10
110001110
001??????
001??????
001??????
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001??????
001???2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001??????
001?2?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001??????
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001?????2
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001????22
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001???222
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001??1222
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001??????
001?21222
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
0011?????
001?21222
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
00111????
001?21222
00112?2??
011111222
01?100000
011100000
110001110
?10001?10
110001110
001110000
001@21222
00112@2@@
011111222
01@100000
011100000
110001110
@10001@10
110001110
YOU WIN!
74 16