update code style

This commit is contained in:
2023-10-07 10:42:54 +08:00
parent b0c7ab0269
commit e4134689c5
3 changed files with 191 additions and 122 deletions

View File

@ -2,3 +2,5 @@ BasedOnStyle: Google
DerivePointerAlignment: false
PointerAlignment: Right
ColumnLimit: 80
AllowCommentMovement: true
PenaltyBreakBeforeFirstCallParameter: 100

View File

@ -1,3 +1,36 @@
/**
* @file client.h
*
* @brief The definition of the client
* @details This file contains the definition of the client. When the function
* ReadMap() is called, the client will read the game map from stdin and trying
* to calculate which blocks definitely has no mine and which blocks definitely
* has mine. Then the client will push the blocks that definitely has no mine
* into a queue no_mine_block_to_be_clicked. When the function Decide() is
* called, the client will pop a block from no_mine_block_to_be_clicked and
* execute it. If no_mine_block_to_be_clicked is empty, the client will make a
* guess.
*
* @codesytle This file is written in a sytle mainly based on Google C++ Style
* Guide. What's sepecial is the comment:
* 1. Multi-line comments are always before the code they comment on.
* Usually the code they comment on is a complex procedure,like the definition
* of a function,a class or a variable with complex operation. If a multi-line
* comment is in one line, it will start with "/*" instead of "/**",otherwise it
* will start with "/**" and in the format of Doxygen.
* 2. Single-line comments are always after the code they comment on.
* Usually they are in the same line with the code they comment on,but sometimes
* they may come in the next lines. single-line comments shouldn't exceed 3
* lines as they are intended to be short and easy to understand.
* 3. Temporary commented code will be marked with "//" in the front of each
* 4. Some comments have special meanings,like "//TODO", "//FIXME", "//XXX","//
* clang-format off" and "// clang-format on". They are not controlled by the
* previous rules.
*
* As I use Clang-format to format my code, so the code style may be a little
* bit strange when parameters or phrases are too long,thus I'm try to manually
* format the code.
*/
#ifndef CLIENT_H
#define CLIENT_H
@ -16,7 +49,7 @@
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.
/*You can not use any other external variables except for rows and columns.*/
/**
* @brief The definition of function Execute(int, int)
@ -55,30 +88,30 @@ void InitGame() {
* 01?
*/
namespace Client {
const unsigned int RndSeed = std::random_device{}();
std::mt19937 RawRnd(RndSeed); // a basic random generator
const double RawRnd_max = 4294967295.0;
const int max_size = 35;
char game_map[max_size][max_size]; // store the raw game map in format of char
std::queue<std::pair<int, int> >
no_mine_block_to_be_clicked; // store the block that definitely has no mine
// and not yet clicked
int map_status[max_size]
[max_size]; // store the status of each block(processed version),
// 0 means unknown , -1 means has mine, 1 means no
// mine and not yet clicked, and 2 means has been
// clicked Note that if some block is found to be
// definitely has no mine or has mine, it will be
// marked as known even if it is not clicked. In
// conclusion, if map_status[i][j] == 0, then
// game_map[i][j] == '?'. If map_status[i][j] == -1,
// then game_map[i][j] == '?', and it will never be
// clicked. If map_status[i][j] == 1, then
// game_map[i][j] == '?', and it will be clicked
// later. If map_status[i][j] == 2, then
// game_map[i][j] == '0'-'8', and it has been clicked
// And when a block's status is updated from 0 to 1,
// it will be pushed into no_mine_block_to_be_clicked
const unsigned int kRndSeed = std::random_device{}();
std::mt19937 RawRnd(kRndSeed); // a basic random generator
const double kRawRndMax = 4294967295.0;
const int kMaxMapSize = 35;
char game_map[kMaxMapSize][kMaxMapSize];
// store the raw game map in format of char
std::queue<std::pair<int, int> > no_mine_block_to_be_clicked;
// store the block that definitely has no mine and not yet clicked
/**
* @brief The definition of variable map_status
*
* @details map_status[kMaxMapSize][kMaxMapSize] stores the status of each
* block(processed version), 0 means unknown , -1 means has mine, 1 means no
* mine and not yet clicked, and 2 means has been clicked Note that if some
* block is found to be definitely has no mine or has mine, it will be marked as
* known even if it is not clicked. In conclusion, if map_status[i][j] == 0,
* then game_map[i][j] == '?'. If map_status[i][j] == -1, then game_map[i][j] ==
* '?', and it will never be clicked. If map_status[i][j]
* == 1, then game_map[i][j] == '?', and it will be clicked later. If
* map_status[i][j] == 2, then game_map[i][j] == '0'-'8', and it has been
* clicked And when a block's status is updated from 0 to 1, it will be pushed
* into no_mine_block_to_be_clicked
*/
int map_status[kMaxMapSize][kMaxMapSize];
/**
* @brief The definition of function ProcessSimpleCase()
*
@ -88,19 +121,19 @@ void ProcessSimpleCase() {
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
if (map_status[i][j] == 2) {
int nearby_mines = game_map[i][j] - '0',
nearby_unkown =
0; // nearby_mines is the number of mines in currently unknown
// blocks that are adjacent to the block (i,j)
int nearby_mines = game_map[i][j] - '0', nearby_unkown = 0;
// nearby_mines is the number of mines in currently unknown blocks that
// are adjacent to the block (i,j)
const int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1},
dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
for (int k = 0; k < 8; k++) {
int x = i + dx[k], y = j + dy[k];
if (x >= 0 && x < rows && y >= 0 && y < columns) {
if (map_status[x][y] == 0)
if (map_status[x][y] == 0) {
nearby_unkown++;
else if (map_status[x][y] == -1)
} else if (map_status[x][y] == -1) {
nearby_mines--;
}
}
}
if (nearby_unkown != 0) {
@ -124,9 +157,8 @@ void ProcessSimpleCase() {
}
}
}
std::map<std::pair<int, int>, int>
position_to_variaID; // convert the (row,column) to variable ID in the
// equations,0 based
std::map<std::pair<int, int>, int> position_to_variaID;
// convert the (row,column) to variable ID in the equations,0 based
std::vector<std::pair<int, int> > variaID_to_position;
/**
* @brief The definition of function PrintEquations()
@ -134,21 +166,23 @@ std::vector<std::pair<int, int> > variaID_to_position;
* @details This function is designed to print the equations for debugging
*/
void PrintEquations(std::vector<std::vector<double> > equations) {
// return;
std::cout << "equations:" << std::endl;
for (int i = 0; i < equations.size(); i++) {
for (int j = 0; j < equations[i].size(); j++)
std::cout << equations[i][j] << " ";
std::cout << std::endl;
}
// use variaID_to_position to print the position of each variable
/*use variaID_to_position to print the position of each variable*/
std::cout << "variaID_to_position:" << std::endl;
// clang-format off
for (int i = 0; i < variaID_to_position.size(); i++)
std::cout << "(" << variaID_to_position[i].first << ","
<< variaID_to_position[i].second << ")"
<< " ";
std::cout << "(" << variaID_to_position[i].first
<< ","
<< variaID_to_position[i].second
<< ") ";
// clang-format on
std::cout << std::endl;
// print map_status
/*print map_status*/
std::cout << "map_status:" << std::endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) std::cout << map_status[i][j] << " ";
@ -209,8 +243,9 @@ std::vector<std::vector<double> > GenerateEquations() {
if (map_status[x][y] == -1) nearby_mines--;
}
}
equations[equations.size() - 1][position_to_variaID.size()] =
nearby_mines;
// clang-format off
equations[equations.size() - 1][position_to_variaID.size()] = nearby_mines;
// clang-format on
for (int k = 0; k < 8; k++) {
int nr = i + dx[k], nc = j + dy[k];
if (nr < 0 || nr >= rows || nc < 0 || nc >= columns) continue;
@ -220,7 +255,7 @@ std::vector<std::vector<double> > GenerateEquations() {
}
}
// PrintEquations(equations);
// randome shuffle lines of equations using RawRnd
/*randome shuffle lines of equations using RawRnd*/
std::random_shuffle(equations.begin(), equations.end(), RandIntLessThan);
return equations;
}
@ -231,14 +266,15 @@ std::vector<std::vector<double> > GenerateEquations() {
* &equations
* @param vector<vector<double>> equations The equations to be solved
*/
const double eps = 1e-6;
const int error_status_of_nearint = -0x3f3f3f3f;
const double kEps = 1e-6;
const int kErrorStatusOfNearbyInt = -0x3f3f3f3f;
inline int NearbyInt(double v) {
int raw = v + 0.5;
if (abs(v - raw) < eps)
if (abs(v - raw) < kEps) {
return raw;
else
return error_status_of_nearint;
} else {
return kErrorStatusOfNearbyInt;
}
}
std::vector<std::vector<double> > GaussianJordanElimination(
std::vector<std::vector<double> > equations) {
@ -257,7 +293,7 @@ std::vector<std::vector<double> > GaussianJordanElimination(
for (int j = i + 1; j < n; j++)
if (abs(equations[j][i]) > abs(equations[pivot][i])) pivot = j;
std::swap(equations[i], equations[pivot]);
if (abs(equations[i][i]) < eps) continue;
if (abs(equations[i][i]) < kEps) continue;
const double pivot_value = equations[i][i];
for (int j = 0; j < m; j++) equations[i][j] /= pivot_value;
for (int j = 0; j < n; j++)
@ -266,13 +302,12 @@ std::vector<std::vector<double> > GaussianJordanElimination(
for (int k = 0; k < m; k++) equations[j][k] -= tmp * equations[i][k];
}
}
// continue;
for (int i = 0; i < equations.size(); i++) {
bool error_occur = false;
int total_num = 0;
for (int j = 0; j < m - 1; j++) {
int v = NearbyInt(equations[i][j]);
if (v == error_status_of_nearint || v < 0) {
if (v == kErrorStatusOfNearbyInt || v < 0) {
error_occur = true;
break;
}
@ -336,18 +371,21 @@ void InterpretResult(std::vector<std::vector<double> > equations) {
if (NearbyInt(equations[i][j]) == 1) {
number_of_1++;
vid = j;
} else if (NearbyInt(equations[i][j]) != 0)
} else if (NearbyInt(equations[i][j]) != 0) {
number_of_non1++;
}
if (number_of_non1) continue;
if (number_of_1 != 1) continue;
int sol = NearbyInt(equations[i][m - 1]);
if (sol == error_status_of_nearint) continue;
if (sol == kErrorStatusOfNearbyInt) continue;
if (sol != 0 && sol != 1) {
std::cerr << "sol=" << sol << std::endl;
std::cerr << "one=" << number_of_1
<< " not one not zero=" << number_of_non1 << std::endl;
std::cerr << NearbyInt(equations[i][m - 1]) << ' ' << equations[i][m - 1]
<< std::endl;
// clang-format off
std::cerr << NearbyInt(equations[i][m - 1]) << ' '
<< equations[i][m - 1] << std::endl;
// clang-format on
PrintEquations(equations);
}
assert(sol == 0 || sol == 1);
@ -381,22 +419,23 @@ void InterpretResult(std::vector<std::vector<double> > equations) {
* it will be marked as known even if it is not clicked.
*/
void PreProcessData() {
using namespace Client;
// scan the game_map and mark clicked block in map_status
/*scan the game_map and mark clicked block in map_status*/
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
if (game_map[i][j] != '?') {
assert(game_map[i][j] >= '0' && game_map[i][j] <= '8');
map_status[i][j] = 2;
}
// scan the map and process the simplest case
/*scan the map and process the simplest case*/
ProcessSimpleCase();
// 1.find all unkown blocks that are adjacnent to clicked blocks and prepare
// for Gaussian-Jordan Elimination.
// 2. start Gaussian-Jordan Elimination
// 3. interpret the result of Gaussian-Jordan Elimination,store the result in
// map_status and push the newly found block that definitely has no mine
// into no_mine_block_to_be_clicked
/**
* 1.find all unkown blocks that are adjacnent to clicked blocks and prepare
* for Gaussian-Jordan Elimination.
* 2. start Gaussian-Jordan Elimination
* 3. interpret the result of Gaussian-Jordan Elimination,store the result in
* map_status and push the newly found block that definitely has no mine
* into no_mine_block_to_be_clicked
*/
for (int i = 0; i < 15; i++) {
std::vector<std::vector<double> > equations = GenerateEquations();
equations = GaussianJordanElimination(equations);
@ -411,10 +450,10 @@ void PreProcessData() {
* just used temporarily before a better algorithm is designed.
*/
std::pair<int, int> TotalRandomGuess() {
using namespace Client;
// std::cout << "TotalRandomGuess" << std::endl;
std::uniform_int_distribution<int> row_dis(0, rows - 1),
column_dis(0, columns - 1);
// clang-format off
std::uniform_int_distribution<int> row_dis(0, rows - 1), column_dis(0, columns - 1);
// clang-format on
int row = row_dis(RawRnd), column = column_dis(RawRnd);
while (map_status[row][column] != 0) {
row = row_dis(RawRnd);
@ -425,11 +464,11 @@ std::pair<int, int> TotalRandomGuess() {
inline double GetProb(double default_p, const std::vector<double> &ps) {
if (ps.empty()) return default_p;
double res = 0;
const double v =
1.75; // use root mean square to estimate the probability in a cautious
// way,and this specific value is chosen by testing
for (int i = 0; i < ps.size(); i++) res += pow(ps[i], v);
return pow(res / ps.size(), 1.0 / v);
const double kPowerMeanCoefficient = 1.75;
// use root mean square to estimate the probability in a cautious way,and this
// specific value is chosen by testing
for (int i = 0; i < ps.size(); i++) res += pow(ps[i], kPowerMeanCoefficient);
return pow(res / ps.size(), 1.0 / kPowerMeanCoefficient);
}
/**
* @brief The definition of function SimpleGuess()
@ -438,12 +477,12 @@ inline double GetProb(double default_p, const std::vector<double> &ps) {
* none-mine block to be clicked using simple algorithm.
*/
std::pair<int, int> SimpleGuess() {
using namespace Client;
const double m_pow_edge = 0.5;
const double m_pow_coner = 2.5;
const double kMappingParameterEdge = 0.5;
const double kMappingParameterCorner = 2.5;
// std::cout << "SimpleGuess" << std::endl;
std::vector<double> probability[max_size][max_size];
double default_probability = pow(0.06, (m_pow_edge + m_pow_coner) / 2);
std::vector<double> probability[kMaxMapSize][kMaxMapSize];
double default_probability =
pow(0.06, (kMappingParameterEdge + kMappingParameterCorner) / 2);
int total_known = 0, total_known_with_mine = 0;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
@ -452,25 +491,26 @@ std::pair<int, int> SimpleGuess() {
if (map_status[i][j] == -1) total_known_with_mine++;
}
if (total_known > 5)
default_probability = pow((double)(total_known_with_mine) / (total_known),
(m_pow_coner + m_pow_edge) / 2);
default_probability =
pow((double)(total_known_with_mine) / (total_known),
(kMappingParameterCorner + kMappingParameterEdge) / 2);
// if((double)(total_known)/(rows*columns)<0.15) return TotalRandomGuess();
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
if (map_status[i][j] == 2) {
int nearby_mines = game_map[i][j] - '0',
nearby_unkown =
0; // nearby_mines is the number of mines in currently unknown
// blocks that are adjacent to the block (i,j)
int nearby_mines = game_map[i][j] - '0', nearby_unkown = 0;
// nearby_mines is the number of mines in currently unknown blocks that
// are adjacent to the block (i,j)
const int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1},
dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
for (int k = 0; k < 8; k++) {
int x = i + dx[k], y = j + dy[k];
if (x >= 0 && x < rows && y >= 0 && y < columns) {
if (map_status[x][y] == 0)
if (map_status[x][y] == 0) {
nearby_unkown++;
else if (map_status[x][y] == -1)
} else if (map_status[x][y] == -1) {
nearby_mines--;
}
}
}
if (nearby_unkown == 0) continue;
@ -478,12 +518,15 @@ std::pair<int, int> SimpleGuess() {
int x = i + dx[k], y = j + dy[k];
if (x >= 0 && x < rows && y >= 0 && y < columns) {
if (map_status[x][y] == 0)
if (abs(dx[k]) + abs(dy[k]) == 2)
if (abs(dx[k]) + abs(dy[k]) == 2) {
probability[x][y].push_back(
pow((double)(nearby_mines) / (nearby_unkown), m_pow_coner));
else
pow((double)(nearby_mines) / (nearby_unkown),
kMappingParameterCorner));
} else {
probability[x][y].push_back(
pow((double)(nearby_mines) / (nearby_unkown), m_pow_edge));
pow((double)(nearby_mines) / (nearby_unkown),
kMappingParameterEdge));
}
assert(abs(dx[k]) + abs(dy[k]) == 1 ||
abs(dx[k]) + abs(dy[k]) == 2);
}
@ -491,8 +534,8 @@ std::pair<int, int> SimpleGuess() {
}
std::pair<int, int> best_guess = TotalRandomGuess();
bool allow_a_guess = true;
const double guess_begin_consideration_ratio = 0.95;
const double guess_tightness_parameter = 10;
const double kRandomBeginConsiderationRatio = 0.95;
const double kRandomTightnessParameter = 10;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
if (map_status[i][j] == 0) {
@ -504,10 +547,10 @@ std::pair<int, int> SimpleGuess() {
best_guess.first = i;
best_guess.second = j;
} else if ((double)(total_known) / (rows * columns) >
guess_begin_consideration_ratio &&
kRandomBeginConsiderationRatio &&
allow_a_guess) {
if (exp(-(this_prob - current_prob) * guess_tightness_parameter) >
RawRnd() / RawRnd_max) {
if (exp(-(this_prob - current_prob) * kRandomTightnessParameter) >
RawRnd() / kRawRndMax) {
best_guess.first = i;
best_guess.second = j;
// allow_a_guess = false;
@ -522,31 +565,25 @@ std::pair<int, int> SimpleGuess() {
* @details This function is designed to make the best guess when there is no
* definite none-mine block to be clicked.
*/
std::pair<int, int> MakeBestGuess() {
using namespace Client;
// just make a total random guess before a better algorithm is designed
// return TotalRandomGuess();
return SimpleGuess();
return std::make_pair(0, 0);
}
inline std::pair<int, int> MakeBestGuess() { return SimpleGuess(); }
/**
* @brief The definition of function GenerateNextStep()
*
* @details This function is designed to generate the next step when playing
* the client's (or player's) role.
*/
std::pair<int, int> GenerateNextStep() {
using namespace Client;
inline std::pair<int, int> GenerateNextStep() {
if (!no_mine_block_to_be_clicked.empty()) {
std::pair<int, int> next_step = no_mine_block_to_be_clicked.front();
no_mine_block_to_be_clicked.pop();
return next_step;
} else
} else {
return MakeBestGuess();
}
}
} // namespace Client
void ReadMap() {
using namespace Client;
using Client::PreProcessData, Client::game_map;
for (int i = 0; i < rows; i++) {
std::cin >> game_map[i];
assert(strlen(game_map[i]) == columns);
@ -561,11 +598,11 @@ void ReadMap() {
* client's (or player's) role. Open up your mind and make your decision here!
*/
void Decide() {
using namespace Client;
using Client::GenerateNextStep;
while (true) {
std::pair<int, int> next_step = GenerateNextStep();
Execute(next_step.first, next_step.second);
}
}
#endif
#endif // CLIENT_H

View File

@ -1,3 +1,30 @@
/**
* @file server.h
*
* @brief The header file of the server.
* @details This file contains the definition of the functions that you need to
* call to run the game.
*
* @codesytle This file is written in a sytle mainly based on Google C++ Style
* Guide. What's sepecial is the comment:
* 1. Multi-line comments are always before the code they comment on.
* Usually the code they comment on is a complex procedure,like the definition
* of a function,a class or a variable with complex operation. If a multi-line
* comment is in one line, it will start with "/*" instead of "/**",otherwise it
* will start with "/**" and in the format of Doxygen.
* 2. Single-line comments are always after the code they comment on.
* Usually they are in the same line with the code they comment on,but sometimes
* they may come in the next lines. single-line comments shouldn't exceed 3
* lines as they are intended to be short and easy to understand.
* 3. Temporary commented code will be marked with "//" in the front of each
* 4. Some comments have special meanings,like "//TODO", "//FIXME", "//XXX","//
* clang-format off" and "// clang-format on". They are not controlled by the
* previous rules.
*
* As I use Clang-format to format my code, so the code style may be a little
* bit strange when parameters or phrases are too long,thus I'm try to manually
* format the code.
*/
#ifndef SERVER_H
#define SERVER_H
@ -14,18 +41,18 @@
* 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.
*/
// This server has passed the formal test, do not modify it if you are not sure
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
namespace Server {
int visit_count, step_count;
const int max_size = 35;
char origin_map[max_size][max_size]; // The original map
char visible_map[max_size][max_size]; // The map that the player can see
int number_of_nearby_mines[max_size][max_size]; // The number of nearby mines
int number_of_all_mines; // The number of all mines
const int kMaxMapSize = 35;
char origin_map[kMaxMapSize][kMaxMapSize]; // The original map
char visible_map[kMaxMapSize][kMaxMapSize]; // The map that the player can see
int number_of_nearby_mines[kMaxMapSize][kMaxMapSize];
// The number of nearby mines
int number_of_all_mines; // The number of all mines
} // namespace Server
/**
* @brief The definition of function InitMap()
@ -40,8 +67,9 @@ int number_of_all_mines; // The number of all mines
* blocks unvisited.
*/
void InitMap() {
using namespace std;
using namespace Server;
using Server::origin_map, Server::visible_map, Server::number_of_nearby_mines,
Server::number_of_all_mines;
using std::cin;
std::cin >> rows >> columns;
assert(2 <= rows && rows <= 30 && 2 <= columns && columns <= 30);
for (int i = 0; i < rows; i++) {
@ -63,7 +91,7 @@ void InitMap() {
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
if (origin_map[i][j] == 'X') number_of_all_mines++;
// bug test: output the number of nearby mines
/* bug test: output the number of nearby mines*/
// for (int i = 0; i < rows; i++) {
// for (int j = 0; j < columns; j++) {
// cout << number_of_nearby_mines[i][j] << ' ';
@ -98,9 +126,11 @@ void InitMap() {
* the game ends and the player loses.
*/
void VisitBlock(int row, int column) {
using namespace Server;
using Server::step_count, Server::origin_map, Server::visible_map,
Server::number_of_nearby_mines, Server::number_of_all_mines,
Server::visit_count;
step_count++;
using namespace std;
using std::queue, std::make_pair, std::pair;
assert(0 <= row && row < rows && 0 <= column && column < columns);
if (origin_map[row][column] == 'X') {
game_state = -1;
@ -161,7 +191,7 @@ void VisitBlock(int row, int column) {
* the advanced task!!!
*/
void PrintMap() {
using namespace Server;
using Server::visible_map, Server::origin_map, Server::number_of_nearby_mines;
if (game_state != 1) {
for (int i = 0; i < rows; i++) {
std::cout << visible_map[i] << std::endl;
@ -189,7 +219,7 @@ void PrintMap() {
* number of steps taken respectively.
*/
void ExitGame() {
using namespace Server;
using Server::visit_count, Server::step_count;
assert(game_state != 0);
if (game_state == 1) {
std::cout << "YOU WIN!" << std::endl;
@ -201,4 +231,4 @@ void ExitGame() {
exit(0); // Exit the game immediately
}
#endif
#endif // SERVER_H