Compare commits
15 Commits
improved-s
...
greedy-ai
Author | SHA1 | Date | |
---|---|---|---|
43df23e1e2 | |||
3b64053e37 | |||
5c03a6b327 | |||
ad0f8c92fa | |||
9f774000c5 | |||
dc2c83cb37 | |||
fec72b8e14 | |||
5e150ec020 | |||
9d390437d5 | |||
37eeba23fb | |||
acf85ab7d9 | |||
53913a8f44 | |||
3f60201c55 | |||
4f7d11b41b | |||
a6bb55639e |
@ -2,5 +2,3 @@ BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Right
|
||||
ColumnLimit: 80
|
||||
AllowCommentMovement: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 100
|
24
README.md
24
README.md
@ -1,27 +1,7 @@
|
||||
# 思路简介
|
||||
## Server
|
||||
相当于一道模拟题,注意时间效率不要太差以及细节问题
|
||||
## Client
|
||||
扫雷一般是采用如下思路:
|
||||
1. 根据已翻开的数字推测周围的哪些格子里有多少雷,在此基础上进行逻辑推理,当推断出n个格子里有0个或n个雷时,我们就可以确定了。优先点开确定的雷。
|
||||
2. 当没有确定的雷点击时,进行一些猜测。
|
||||
|
||||
### 确定解
|
||||
在尝试寻找确定解时,我采用的方式是在每个待求解格子上设一个未知数,解一个方程组,在高斯——约旦消元的基础上特判一下n个格子里有0个或n个雷,由于这个特判过程不是基本行变换,对线性方程的影响可能不太可靠,多求解几次。
|
||||
|
||||
### 猜解
|
||||
1. 在猜测时,对于一个已翻开的格子,如果它周围的n个未知格子里应当有m个雷,那么把$\frac{m}{n}$用幂函数$f(x)=x^k ([0,1]\to [0,1])$处理一下,并把处理结果塞进那些未知格。对于已知格和未知格为边相邻和角相邻,$f(x)$分两种,它们的幂次不同。这个步骤的目的是赋予边相邻和角相邻的情况不同的权重。
|
||||
2. 当一个格没有收集到任何数时,采用一个全局默认估计有雷频率(初始为0.06,当已翻开格子足够多后用已知区实际计算比例)替代$\frac{m}{n}$,幂函数的幂次为两种幂次的算术平均数,把全局默认估计有雷频率用幂函数处理一下视作这个格子收到的数。
|
||||
3. 随后,对于每个未知格,把它收集到的数取一个t次方平均数,由此得到最终估计有雷概率。这个步骤的目的是赋予较大的值更大的权重,这样更加慎重一点。
|
||||
4. 在得到最终估计有雷概率后,扫描整个地图,找严格概率最小的那个格子。但是在还剩最后5%的格子没有被点开时,仿照模拟退火以一定可能性允许概率更大的格子替换当前解
|
||||
|
||||
___
|
||||
# references目录下的参考信息来源
|
||||
~~虽然好像根本没看~~
|
||||
- <https://github.com/DavidNHill/Minesweeper>
|
||||
- <https://github.com/DavidNHill/Minesweeper2>
|
||||
- <https://dash.harvard.edu/bitstream/handle/1/14398552/BECERRA-SENIORTHESIS-2015.pdf>
|
||||
|
||||
___
|
||||
# Minesweeper-2023
|
||||
> ACM 班 2023 级程序设计第一次大作业
|
||||
@ -242,10 +222,6 @@ Execute(row, column):
|
||||
|
||||
请在 OnlineJudge 上直接提交你实现后的头文件。对于基础任务,提交 `server.h`,对于进阶任务,提交 `client.h`。
|
||||
|
||||
### 数据范围
|
||||
|
||||
保证 `n` `m` 均不大于 30,操作序列长度不大于 1000。
|
||||
|
||||
### 评分规则
|
||||
|
||||
A班、B班均有最多 10% 的 bonus 分数。
|
||||
|
@ -8,3 +8,4 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
add_executable(server main.cpp)
|
||||
|
||||
add_executable(client advanced.cpp) # For advanced task
|
||||
add_executable(tablegenerator tablegenerator.cpp)
|
@ -1,214 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
import sys
|
||||
import os
|
||||
import random
|
||||
built_in_test_cases = [
|
||||
"""10 10
|
||||
........X.
|
||||
..........
|
||||
..........
|
||||
......X...
|
||||
..........
|
||||
.X.......X
|
||||
..........
|
||||
X........X
|
||||
..........
|
||||
X....XX...
|
||||
0 0""",
|
||||
"""10 10
|
||||
.....X...X
|
||||
.........X
|
||||
X..X......
|
||||
X.X.......
|
||||
..X.......
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
X.........
|
||||
0 0""",
|
||||
"""10 10
|
||||
.XX.......
|
||||
...X...XX.
|
||||
..........
|
||||
...X......
|
||||
.X......X.
|
||||
..X....X..
|
||||
XX....X...
|
||||
..X...X...
|
||||
..X.X.X.X.
|
||||
....X.....
|
||||
0 9""",
|
||||
"""10 10
|
||||
.......X..
|
||||
X.....X...
|
||||
X...X.....
|
||||
...X......
|
||||
..........
|
||||
.X.X.X....
|
||||
..X.X..X..
|
||||
.X.X.....X
|
||||
....X.....
|
||||
.....X....
|
||||
4 9""",
|
||||
"""20 20
|
||||
X...........X.......
|
||||
..XX.............X.X
|
||||
.X......X.........X.
|
||||
....X.X...X...X.....
|
||||
....X.X.........X...
|
||||
X.XX...........X.X..
|
||||
...X.............XX.
|
||||
...XX...X..X..X.X...
|
||||
...X.X...X...X......
|
||||
.............XX.X...
|
||||
.X............X.....
|
||||
.X..X.........X....X
|
||||
X.X.X..X...X..X.....
|
||||
.X....X.X......X....
|
||||
...X.........X..X...
|
||||
...X.X...X..........
|
||||
..X.XX.X......XXXX..
|
||||
.X.X...............X
|
||||
XXX..X....X.XX..X...
|
||||
.X...X.XX........X.X
|
||||
0 10""",
|
||||
"""10 10
|
||||
.XX.......
|
||||
...X...XX.
|
||||
..........
|
||||
...X......
|
||||
.X......X.
|
||||
..X....X..
|
||||
XX....X...
|
||||
..X...X...
|
||||
..X.X.X.X.
|
||||
....X.....
|
||||
3 6""",
|
||||
"""20 20
|
||||
X...........X.......
|
||||
..XX.............X.X
|
||||
.X......X.........X.
|
||||
....X.X...X...X.....
|
||||
....X.X.........X...
|
||||
X.XX...........X.X..
|
||||
...X.............XX.
|
||||
...XX...X..X..X.X...
|
||||
...X.X...X...X......
|
||||
.............XX.X...
|
||||
.X............X.....
|
||||
.X..X.........X....X
|
||||
X.X.X..X...X..X.....
|
||||
.X....X.X......X....
|
||||
...X.........X..X...
|
||||
...X.X...X..........
|
||||
..X.XX.X......XXXX..
|
||||
.X.X...............X
|
||||
XXX..X....X.XX..X...
|
||||
.X...X.XX........X.X
|
||||
4 12"""
|
||||
]
|
||||
mine_rate=0.133
|
||||
|
||||
def ATest():
|
||||
total_round=0
|
||||
win_round=0
|
||||
for i in range(0,10):
|
||||
for data in built_in_test_cases:
|
||||
fn=open("tmp/test.in","w")
|
||||
fn.write(data)
|
||||
fn.close()
|
||||
os.system("tmp/client < tmp/test.in > tmp/test.out")
|
||||
fn=open("tmp/test.out","r")
|
||||
lines=fn.readlines()
|
||||
fn.close()
|
||||
print(lines)
|
||||
total_round+=1
|
||||
if len(lines)>0:
|
||||
if lines[0]=='YOU WIN!\n':
|
||||
win_round+=1
|
||||
print("win rate: "+str(win_round/total_round),win_round,total_round)
|
||||
|
||||
#input("Press Enter to continue...")
|
||||
|
||||
for loop_cnt in range(10000):
|
||||
# randomly generate n,m in [2,30]
|
||||
n=random.randint(20,30)
|
||||
m=random.randint(20,30)
|
||||
# print(n,m)
|
||||
# randomly generate mine_rate in [0,1]
|
||||
#mine_rate=random.random()
|
||||
# generate a random map
|
||||
map=[]
|
||||
for i in range(n):
|
||||
map.append([])
|
||||
for j in range(m):
|
||||
if random.random()<mine_rate:
|
||||
map[i].append('X')
|
||||
else:
|
||||
map[i].append('.')
|
||||
# generate a random start point with no mine
|
||||
sx=random.randint(0,n-1)
|
||||
sy=random.randint(0,m-1)
|
||||
while map[sx][sy]=='X':
|
||||
sx=random.randint(0,n-1)
|
||||
sy=random.randint(0,m-1)
|
||||
# output the map
|
||||
fn=open("tmp/test.in","w")
|
||||
fn.write(str(n)+" "+str(m)+"\n")
|
||||
for i in range(n):
|
||||
for j in range(m):
|
||||
fn.write(map[i][j])
|
||||
fn.write("\n")
|
||||
fn.write(str(sx)+" "+str(sy))
|
||||
fn.close()
|
||||
# run the program
|
||||
os.system("tmp/client < tmp/test.in > tmp/test.out")
|
||||
# read the output
|
||||
fn=open("tmp/test.out","r")
|
||||
lines=fn.readlines()
|
||||
fn.close()
|
||||
# check the output
|
||||
print(lines)
|
||||
total_round+=1
|
||||
if len(lines)>0:
|
||||
if lines[0]=='YOU WIN!\n':
|
||||
win_round+=1
|
||||
print("win rate: "+str(win_round/total_round),win_round,total_round)
|
||||
return win_round/total_round
|
||||
|
||||
value1_list=[0.25,0.5,0.75,1,1.25,1.5,1.75,2,2.25,2.5,3]
|
||||
value2_list=[0.25,0.5,0.75,1,1.25,1.5,1.75,2,2.25,2.5,3]
|
||||
def GenerateSource(value1,value2):
|
||||
# read src/include/client.h
|
||||
fn=open("src/include/client.h","r")
|
||||
lines=fn.readlines()
|
||||
fn.close()
|
||||
# change the 441 th line to "const double m_pow_edge = "+str(value1)+";"
|
||||
lines[440]="const double m_pow_edge = "+str(value1)+";\n"
|
||||
# change the 442 th line to "const double m_pow_coner = "+str(value2)+";"
|
||||
lines[441]="const double m_pow_coner = "+str(value2)+";\n"
|
||||
# write the source to "tmp/client.h"
|
||||
fn=open("tmp/client.h","w")
|
||||
fn.writelines(lines)
|
||||
fn.close()
|
||||
# copy src/include/server.h to tmp/server.h
|
||||
os.system("cp src/include/server.h tmp/server.h")
|
||||
# copy src/advanced.cpp to tmp/advanced.cpp
|
||||
os.system("cp src/advanced.cpp tmp/advanced.cpp")
|
||||
# compile the source tmp/advanced.cpp to tmp/client
|
||||
os.system("g++ -std=c++11 -g -O2 -Wall -Wextra tmp/advanced.cpp -o tmp/client")
|
||||
|
||||
for value1 in value1_list:
|
||||
for value2 in value2_list:
|
||||
GenerateSource(value1,value2)
|
||||
print("value1="+str(value1)+", value2="+str(value2))
|
||||
ratio=ATest()
|
||||
print("win rate: "+str(ratio))
|
||||
print("")
|
||||
# write to tmp/log.txt
|
||||
fn=open("tmp/log.txt","a")
|
||||
fn.write("value1="+str(value1)+", value2="+str(value2)+"\n")
|
||||
fn.write("win rate: "+str(ratio)+"\n")
|
||||
fn.write("\n")
|
||||
fn.close()
|
@ -111,7 +111,7 @@ XXX..X....X.XX..X...
|
||||
mine_rate=0.133
|
||||
total_round=0
|
||||
win_round=0
|
||||
for i in range(0,10):
|
||||
for i in range(0,1):
|
||||
for data in built_in_test_cases:
|
||||
fn=open("tmp/test.in","w")
|
||||
fn.write(data)
|
||||
@ -122,7 +122,6 @@ for i in range(0,10):
|
||||
fn.close()
|
||||
print(lines)
|
||||
total_round+=1
|
||||
if len(lines)>0:
|
||||
if lines[0]=='YOU WIN!\n':
|
||||
win_round+=1
|
||||
print("win rate: "+str(win_round/total_round),win_round,total_round)
|
||||
@ -169,7 +168,6 @@ while True:
|
||||
# check the output
|
||||
print(lines)
|
||||
total_round+=1
|
||||
if len(lines)>0:
|
||||
if lines[0]=='YOU WIN!\n':
|
||||
win_round+=1
|
||||
print("win rate: "+str(win_round/total_round),win_round,total_round)
|
@ -1,36 +1,3 @@
|
||||
/**
|
||||
* @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
|
||||
|
||||
@ -43,13 +10,115 @@
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "data.h"
|
||||
#include "zb64.h"
|
||||
namespace DataLoad {
|
||||
typedef long long LL;
|
||||
const int buf_size = 4412555 * 4;
|
||||
unsigned char buf[buf_size], data[buf_size];
|
||||
bool already_have[14348907];
|
||||
// std::unordered_map<LL,unsigned char> visible_to_probability;
|
||||
int rid[15] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2};
|
||||
int cid[15] = {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4};
|
||||
class HashTable {
|
||||
static const int buf_size = 44348907;
|
||||
struct Node {
|
||||
LL key;
|
||||
unsigned char value;
|
||||
Node *next;
|
||||
// Node(LL k, unsigned char v) : key(k), value(v), next(nullptr) {}
|
||||
};
|
||||
Node *table[buf_size], mem[buf_size], *cur = mem;
|
||||
|
||||
public:
|
||||
HashTable() {
|
||||
for (int i = 0; i < buf_size; i++) table[i] = nullptr;
|
||||
}
|
||||
~HashTable() {}
|
||||
unsigned char &operator[](LL key) {
|
||||
int index = key % buf_size;
|
||||
Node *p = table[index];
|
||||
while (p) {
|
||||
if (p->key == key) return p->value;
|
||||
p = p->next;
|
||||
}
|
||||
p = cur++;
|
||||
p->key = key;
|
||||
p->next = table[index];
|
||||
table[index] = p;
|
||||
return p->value;
|
||||
}
|
||||
bool have(LL key) {
|
||||
int index = key % buf_size;
|
||||
Node *p = table[index];
|
||||
while (p) {
|
||||
if (p->key == key) return true;
|
||||
p = p->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} visible_to_probability;
|
||||
} // namespace DataLoad
|
||||
void LoadData() {
|
||||
using namespace DataLoad;
|
||||
size_t raw_length = base64_decode(pre_calc_prob, buf, buf_size);
|
||||
size_t data_length = decompressData(buf, raw_length, data, buf_size);
|
||||
// std::cout<<"decompress finished.\n"<<std::endl;
|
||||
// already_have.rehash(4412555);
|
||||
// visible_to_probability.rehash(4412555);
|
||||
const LL raw_line_base = 243;
|
||||
const LL vis_line_base = 100000;
|
||||
int cnt = 0;
|
||||
for (int status = 0; status < 14348907; status++) {
|
||||
LL inverse_status =
|
||||
(status % raw_line_base) * raw_line_base * raw_line_base +
|
||||
((status / raw_line_base) % raw_line_base) * raw_line_base +
|
||||
(status / (raw_line_base * raw_line_base));
|
||||
if (already_have[inverse_status]) continue;
|
||||
// assert(already_have.find(status) == already_have.end());
|
||||
already_have[status] = true;
|
||||
int inner_mp[3][5] = {0}, visible_map[3][5];
|
||||
LL status_tmp = status;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int row = rid[i], col = cid[i];
|
||||
inner_mp[row][col] = (status_tmp % 3); // uncode the inner_status
|
||||
status_tmp /= 3;
|
||||
}
|
||||
for (int row = 0; row < 3; row++)
|
||||
for (int col = 0; col < 5; col++) {
|
||||
if (inner_mp[row][col] == 0 || inner_mp[row][col] == 1) {
|
||||
visible_map[row][col] = 9; // 9 means unshown to player
|
||||
} else {
|
||||
int mcnt = 0;
|
||||
const int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1},
|
||||
dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int nr = row + dx[i], nc = col + dy[i];
|
||||
if (nr < 0 || nr >= 3 || nc < 0 || nc >= 5) continue;
|
||||
mcnt += (inner_mp[nr][nc] == 0 ? 1 : 0);
|
||||
}
|
||||
visible_map[row][col] = mcnt;
|
||||
}
|
||||
}
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int row = rid[i], col = cid[i];
|
||||
visible_status = visible_status * 10 + visible_map[row][col];
|
||||
}
|
||||
visible_to_probability[visible_status] = data[cnt++];
|
||||
}
|
||||
// std::cout<<"Load data finished.\n"<<std::endl;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -71,6 +140,7 @@ void Execute(int row, int column);
|
||||
* map and the first step taken by the server (see README).
|
||||
*/
|
||||
void InitGame() {
|
||||
LoadData();
|
||||
int first_row, first_column;
|
||||
std::cin >> first_row >> first_column;
|
||||
Execute(first_row, first_column);
|
||||
@ -88,30 +158,29 @@ void InitGame() {
|
||||
* 01?
|
||||
*/
|
||||
namespace Client {
|
||||
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];
|
||||
const unsigned int RndSeed = std::random_device{}();
|
||||
std::mt19937 RawRnd(RndSeed); // a basic random generator
|
||||
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
|
||||
/**
|
||||
* @brief The definition of function ProcessSimpleCase()
|
||||
*
|
||||
@ -121,21 +190,21 @@ 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) {
|
||||
if (nearby_mines == 0) {
|
||||
for (int k = 0; k < 8; k++) {
|
||||
@ -157,8 +226,9 @@ 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()
|
||||
@ -166,23 +236,21 @@ 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
|
||||
<< ") ";
|
||||
// clang-format on
|
||||
std::cout << "(" << variaID_to_position[i].first << ","
|
||||
<< variaID_to_position[i].second << ")"
|
||||
<< " ";
|
||||
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] << " ";
|
||||
@ -243,9 +311,8 @@ std::vector<std::vector<double> > GenerateEquations() {
|
||||
if (map_status[x][y] == -1) nearby_mines--;
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
equations[equations.size() - 1][position_to_variaID.size()] = nearby_mines;
|
||||
// clang-format on
|
||||
equations[equations.size() - 1][position_to_variaID.size()] =
|
||||
nearby_mines;
|
||||
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;
|
||||
@ -255,7 +322,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;
|
||||
}
|
||||
@ -266,15 +333,14 @@ std::vector<std::vector<double> > GenerateEquations() {
|
||||
* &equations
|
||||
* @param vector<vector<double>> equations The equations to be solved
|
||||
*/
|
||||
const double kEps = 1e-6;
|
||||
const int kErrorStatusOfNearbyInt = -0x3f3f3f3f;
|
||||
const double eps = 1e-6;
|
||||
const int error_status_of_nearint = -0x3f3f3f3f;
|
||||
inline int NearbyInt(double v) {
|
||||
int raw = v + 0.5;
|
||||
if (abs(v - raw) < kEps) {
|
||||
if (abs(v - raw) < eps)
|
||||
return raw;
|
||||
} else {
|
||||
return kErrorStatusOfNearbyInt;
|
||||
}
|
||||
else
|
||||
return error_status_of_nearint;
|
||||
}
|
||||
std::vector<std::vector<double> > GaussianJordanElimination(
|
||||
std::vector<std::vector<double> > equations) {
|
||||
@ -285,7 +351,7 @@ std::vector<std::vector<double> > GaussianJordanElimination(
|
||||
std::vector<double> equa_template;
|
||||
equa_template.resize(m);
|
||||
// assert(n + 1 == m);
|
||||
for (int tot = 0; tot < 10; tot++) {
|
||||
for (int tot = 0; tot < 5; tot++) {
|
||||
n = equations.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int pivot = i;
|
||||
@ -293,7 +359,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]) < kEps) continue;
|
||||
if (abs(equations[i][i]) < eps) 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++)
|
||||
@ -302,12 +368,13 @@ 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 == kErrorStatusOfNearbyInt || v < 0) {
|
||||
if (v == error_status_of_nearint || v < 0) {
|
||||
error_occur = true;
|
||||
break;
|
||||
}
|
||||
@ -371,21 +438,18 @@ 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 == kErrorStatusOfNearbyInt) continue;
|
||||
if (sol == error_status_of_nearint) 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;
|
||||
// clang-format off
|
||||
std::cerr << NearbyInt(equations[i][m - 1]) << ' '
|
||||
<< equations[i][m - 1] << std::endl;
|
||||
// clang-format on
|
||||
std::cerr << NearbyInt(equations[i][m - 1]) << ' ' << equations[i][m - 1]
|
||||
<< std::endl;
|
||||
PrintEquations(equations);
|
||||
}
|
||||
assert(sol == 0 || sol == 1);
|
||||
@ -419,24 +483,23 @@ void InterpretResult(std::vector<std::vector<double> > equations) {
|
||||
* it will be marked as known even if it is not clicked.
|
||||
*/
|
||||
void PreProcessData() {
|
||||
/*scan the game_map and mark clicked block in map_status*/
|
||||
using namespace Client;
|
||||
// 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
|
||||
*/
|
||||
for (int i = 0; i < 15; i++) {
|
||||
// 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 < 8; i++) {
|
||||
std::vector<std::vector<double> > equations = GenerateEquations();
|
||||
equations = GaussianJordanElimination(equations);
|
||||
InterpretResult(equations);
|
||||
@ -450,10 +513,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;
|
||||
// clang-format off
|
||||
std::uniform_int_distribution<int> row_dis(0, rows - 1), column_dis(0, columns - 1);
|
||||
// clang-format on
|
||||
std::uniform_int_distribution<int> row_dis(0, rows - 1),
|
||||
column_dis(0, columns - 1);
|
||||
int row = row_dis(RawRnd), column = column_dis(RawRnd);
|
||||
while (map_status[row][column] != 0) {
|
||||
row = row_dis(RawRnd);
|
||||
@ -464,11 +527,10 @@ 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 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);
|
||||
const double v =
|
||||
2; // use root mean square to estimate the probability in a cautious way
|
||||
for (int i = 0; i < ps.size(); i++) res += pow(ps[i], v);
|
||||
return pow(res / ps.size(), 1.0 / v);
|
||||
}
|
||||
/**
|
||||
* @brief The definition of function SimpleGuess()
|
||||
@ -477,12 +539,10 @@ 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() {
|
||||
const double kMappingParameterEdge = 0.5;
|
||||
const double kMappingParameterCorner = 2.5;
|
||||
using namespace Client;
|
||||
// std::cout << "SimpleGuess" << std::endl;
|
||||
std::vector<double> probability[kMaxMapSize][kMaxMapSize];
|
||||
double default_probability =
|
||||
pow(0.06, (kMappingParameterEdge + kMappingParameterCorner) / 2);
|
||||
std::vector<double> probability[max_size][max_size];
|
||||
double default_probability = 0.06;
|
||||
int total_known = 0, total_known_with_mine = 0;
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < columns; j++)
|
||||
@ -491,51 +551,37 @@ 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),
|
||||
(kMappingParameterCorner + kMappingParameterEdge) / 2);
|
||||
default_probability = (double)(total_known_with_mine) / (total_known);
|
||||
// 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;
|
||||
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 (abs(dx[k]) + abs(dy[k]) == 2) {
|
||||
probability[x][y].push_back(
|
||||
pow((double)(nearby_mines) / (nearby_unkown),
|
||||
kMappingParameterCorner));
|
||||
} else {
|
||||
probability[x][y].push_back(
|
||||
pow((double)(nearby_mines) / (nearby_unkown),
|
||||
kMappingParameterEdge));
|
||||
}
|
||||
assert(abs(dx[k]) + abs(dy[k]) == 1 ||
|
||||
abs(dx[k]) + abs(dy[k]) == 2);
|
||||
probability[x][y].push_back((double)(nearby_mines) /
|
||||
(nearby_unkown));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::pair<int, int> best_guess = TotalRandomGuess();
|
||||
bool allow_a_guess = true;
|
||||
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) {
|
||||
@ -546,44 +592,171 @@ std::pair<int, int> SimpleGuess() {
|
||||
if (this_prob < current_prob) {
|
||||
best_guess.first = i;
|
||||
best_guess.second = j;
|
||||
} else if ((double)(total_known) / (rows * columns) >
|
||||
kRandomBeginConsiderationRatio &&
|
||||
allow_a_guess) {
|
||||
if (exp(-(this_prob - current_prob) * kRandomTightnessParameter) >
|
||||
RawRnd() / kRawRndMax) {
|
||||
best_guess.first = i;
|
||||
best_guess.second = j;
|
||||
// allow_a_guess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best_guess;
|
||||
}
|
||||
double EstimateProb(std::pair<int, int> pos, double default_p = 0.06) {
|
||||
if (pos.first == 0 || pos.first == rows - 1 || pos.second == 0 ||
|
||||
pos.second == columns - 1)
|
||||
return default_p;
|
||||
std::vector<double> ps;
|
||||
double res = 0;
|
||||
typedef long long LL;
|
||||
const LL raw_line_base = 243;
|
||||
const LL vis_line_base = 100000;
|
||||
int rid[15] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2};
|
||||
int cid[15] = {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4};
|
||||
if (pos.second + 3 <= columns - 1) {
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int x = pos.first + rid[i] - 1, y = pos.second + cid[i] - 1;
|
||||
if (map_status[x][y] != 2)
|
||||
visible_status = (visible_status * 10) + 9;
|
||||
else if (map_status[x][y] == 2)
|
||||
visible_status = (visible_status * 10) + game_map[x][y] - '0';
|
||||
}
|
||||
LL invers_vis_status =
|
||||
(visible_status % vis_line_base) * vis_line_base * vis_line_base +
|
||||
((visible_status / vis_line_base) % vis_line_base) * vis_line_base +
|
||||
visible_status / (vis_line_base * vis_line_base);
|
||||
if (DataLoad::visible_to_probability.have(visible_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[visible_status] / 255.0);
|
||||
if (DataLoad::visible_to_probability.have(invers_vis_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[invers_vis_status] / 255.0);
|
||||
}
|
||||
if (pos.second - 3 >= 0) {
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int x = pos.first + rid[i] - 1, y = pos.second - (cid[i] - 1);
|
||||
if (map_status[x][y] != 2)
|
||||
visible_status = (visible_status * 10) + 9;
|
||||
else if (map_status[x][y] == 2)
|
||||
visible_status = (visible_status * 10) + game_map[x][y] - '0';
|
||||
}
|
||||
LL invers_vis_status =
|
||||
(visible_status % vis_line_base) * vis_line_base * vis_line_base +
|
||||
((visible_status / vis_line_base) % vis_line_base) * vis_line_base +
|
||||
visible_status / (vis_line_base * vis_line_base);
|
||||
if (DataLoad::visible_to_probability.have(visible_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[visible_status] / 255.0);
|
||||
if (DataLoad::visible_to_probability.have(invers_vis_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[invers_vis_status] / 255.0);
|
||||
}
|
||||
if (pos.first + 3 <= rows - 1) {
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int x = pos.first + cid[i] - 1, y = pos.second + rid[i] - 1;
|
||||
if (map_status[x][y] != 2)
|
||||
visible_status = (visible_status * 10) + 9;
|
||||
else if (map_status[x][y] == 2)
|
||||
visible_status = (visible_status * 10) + game_map[x][y] - '0';
|
||||
}
|
||||
LL invers_vis_status =
|
||||
(visible_status % vis_line_base) * vis_line_base * vis_line_base +
|
||||
((visible_status / vis_line_base) % vis_line_base) * vis_line_base +
|
||||
visible_status / (vis_line_base * vis_line_base);
|
||||
if (DataLoad::visible_to_probability.have(visible_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[visible_status] / 255.0);
|
||||
if (DataLoad::visible_to_probability.have(invers_vis_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[invers_vis_status] / 255.0);
|
||||
}
|
||||
if (pos.first - 3 >= 0) {
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int x = pos.first - (cid[i] - 1), y = pos.second + rid[i] - 1;
|
||||
if (map_status[x][y] != 2)
|
||||
visible_status = (visible_status * 10) + 9;
|
||||
else if (map_status[x][y] == 2)
|
||||
visible_status = (visible_status * 10) + game_map[x][y] - '0';
|
||||
}
|
||||
LL invers_vis_status =
|
||||
(visible_status % vis_line_base) * vis_line_base * vis_line_base +
|
||||
((visible_status / vis_line_base) % vis_line_base) * vis_line_base +
|
||||
visible_status / (vis_line_base * vis_line_base);
|
||||
if (DataLoad::visible_to_probability.have(visible_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[visible_status] / 255.0);
|
||||
if (DataLoad::visible_to_probability.have(invers_vis_status))
|
||||
ps.push_back(DataLoad::visible_to_probability[invers_vis_status] / 255.0);
|
||||
}
|
||||
// assert(ps.size() > 0);
|
||||
if(ps.size()==0)
|
||||
{
|
||||
;
|
||||
return default_p;
|
||||
}
|
||||
for (int i = 0; i < ps.size(); i++) res += ps[i];
|
||||
return res / ps.size();
|
||||
}
|
||||
/**
|
||||
* @brief The definition of function GreedyGuess()
|
||||
*
|
||||
* @details This function is designed to make a guess when there is no definite
|
||||
*/
|
||||
std::pair<int, int> GreedyGuess() {
|
||||
double default_probability = 0.06;
|
||||
int total_known = 0, total_known_with_mine = 0;
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < columns; j++)
|
||||
if (map_status[i][j] != 0) {
|
||||
total_known++;
|
||||
if (map_status[i][j] == -1) total_known_with_mine++;
|
||||
}
|
||||
if (total_known > 5)
|
||||
default_probability = (double)(total_known_with_mine) / (total_known);
|
||||
std::pair<int, int> res;
|
||||
bool is_first = true;
|
||||
double res_prob = 1;
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < columns; j++)
|
||||
if (map_status[i][j] == 0) {
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
res = std::make_pair(i, j);
|
||||
double res_prob = EstimateProb(res, default_probability);
|
||||
continue;
|
||||
}
|
||||
double this_prob =
|
||||
EstimateProb(std::make_pair(i, j), default_probability);
|
||||
if (this_prob < res_prob) {
|
||||
res = std::make_pair(i, j);
|
||||
res_prob = this_prob;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
* @brief The definition of function MakeBestGuess()
|
||||
*
|
||||
* @details This function is designed to make the best guess when there is no
|
||||
* definite none-mine block to be clicked.
|
||||
*/
|
||||
inline std::pair<int, int> MakeBestGuess() { return SimpleGuess(); }
|
||||
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 GreedyGuess();
|
||||
return std::make_pair(0, 0);
|
||||
}
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
inline std::pair<int, int> GenerateNextStep() {
|
||||
std::pair<int, int> GenerateNextStep() {
|
||||
using namespace Client;
|
||||
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 Client::PreProcessData, Client::game_map;
|
||||
using namespace Client;
|
||||
for (int i = 0; i < rows; i++) {
|
||||
std::cin >> game_map[i];
|
||||
assert(strlen(game_map[i]) == columns);
|
||||
@ -598,11 +771,11 @@ void ReadMap() {
|
||||
* client's (or player's) role. Open up your mind and make your decision here!
|
||||
*/
|
||||
void Decide() {
|
||||
using Client::GenerateNextStep;
|
||||
using namespace Client;
|
||||
while (true) {
|
||||
std::pair<int, int> next_step = GenerateNextStep();
|
||||
Execute(next_step.first, next_step.second);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CLIENT_H
|
||||
#endif
|
1
src/include/data.h
Normal file
1
src/include/data.h
Normal file
File diff suppressed because one or more lines are too long
@ -1,30 +1,3 @@
|
||||
/**
|
||||
* @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
|
||||
|
||||
@ -41,17 +14,17 @@
|
||||
* 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 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
|
||||
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
|
||||
} // namespace Server
|
||||
/**
|
||||
@ -67,9 +40,8 @@ int number_of_all_mines; // The number of all mines
|
||||
* blocks unvisited.
|
||||
*/
|
||||
void InitMap() {
|
||||
using Server::origin_map, Server::visible_map, Server::number_of_nearby_mines,
|
||||
Server::number_of_all_mines;
|
||||
using std::cin;
|
||||
using namespace std;
|
||||
using namespace Server;
|
||||
std::cin >> rows >> columns;
|
||||
assert(2 <= rows && rows <= 30 && 2 <= columns && columns <= 30);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
@ -91,7 +63,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] << ' ';
|
||||
@ -126,11 +98,9 @@ void InitMap() {
|
||||
* the game ends and the player loses.
|
||||
*/
|
||||
void VisitBlock(int row, int column) {
|
||||
using Server::step_count, Server::origin_map, Server::visible_map,
|
||||
Server::number_of_nearby_mines, Server::number_of_all_mines,
|
||||
Server::visit_count;
|
||||
using namespace Server;
|
||||
step_count++;
|
||||
using std::queue, std::make_pair, std::pair;
|
||||
using namespace std;
|
||||
assert(0 <= row && row < rows && 0 <= column && column < columns);
|
||||
if (origin_map[row][column] == 'X') {
|
||||
game_state = -1;
|
||||
@ -191,7 +161,7 @@ void VisitBlock(int row, int column) {
|
||||
* the advanced task!!!
|
||||
*/
|
||||
void PrintMap() {
|
||||
using Server::visible_map, Server::origin_map, Server::number_of_nearby_mines;
|
||||
using namespace Server;
|
||||
if (game_state != 1) {
|
||||
for (int i = 0; i < rows; i++) {
|
||||
std::cout << visible_map[i] << std::endl;
|
||||
@ -219,7 +189,7 @@ void PrintMap() {
|
||||
* number of steps taken respectively.
|
||||
*/
|
||||
void ExitGame() {
|
||||
using Server::visit_count, Server::step_count;
|
||||
using namespace Server;
|
||||
assert(game_state != 0);
|
||||
if (game_state == 1) {
|
||||
std::cout << "YOU WIN!" << std::endl;
|
||||
@ -231,4 +201,4 @@ void ExitGame() {
|
||||
exit(0); // Exit the game immediately
|
||||
}
|
||||
|
||||
#endif // SERVER_H
|
||||
#endif
|
||||
|
8885
src/include/zb64.h
Normal file
8885
src/include/zb64.h
Normal file
File diff suppressed because it is too large
Load Diff
153
src/tablegenerator.cpp
Normal file
153
src/tablegenerator.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "zb64.h"
|
||||
|
||||
using namespace std;
|
||||
typedef long long LL;
|
||||
int rid[15] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2};
|
||||
int cid[15] = {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4};
|
||||
// unordered_map<LL,LL> visible_to_inner;
|
||||
unordered_map<LL, vector<LL>> visible_to_inner;
|
||||
unordered_map<LL, LL> inner_to_visible;
|
||||
vector<LL> valid_visible_status;
|
||||
unordered_map<LL, double> visible_to_probability;
|
||||
unordered_set<LL> already_have;
|
||||
void FindStatus() {
|
||||
const LL raw_line_base = 243;
|
||||
const LL vis_line_base = 100000;
|
||||
for (int status = 0; status < 14348907; status++) {
|
||||
int inner_mp[3][5] = {0}, visible_map[3][5];
|
||||
LL status_tmp = status;
|
||||
LL inverse_status =
|
||||
(status % raw_line_base) * raw_line_base * raw_line_base +
|
||||
((status / raw_line_base) % raw_line_base) * raw_line_base +
|
||||
(status / (raw_line_base * raw_line_base));
|
||||
if (already_have.find(inverse_status) != already_have.end()) continue;
|
||||
assert(already_have.find(status) == already_have.end());
|
||||
already_have.insert(status);
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int row = rid[i], col = cid[i];
|
||||
inner_mp[row][col] = (status_tmp % 3); // uncode the inner_status
|
||||
status_tmp /= 3;
|
||||
}
|
||||
for (int row = 0; row < 3; row++)
|
||||
for (int col = 0; col < 5; col++) {
|
||||
if (inner_mp[row][col] == 0 || inner_mp[row][col] == 1) {
|
||||
visible_map[row][col] = 9; // 9 means unshown to player
|
||||
} else {
|
||||
int mcnt = 0;
|
||||
const int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1},
|
||||
dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int nr = row + dx[i], nc = col + dy[i];
|
||||
if (nr < 0 || nr >= 3 || nc < 0 || nc >= 5) continue;
|
||||
mcnt += (inner_mp[nr][nc] == 0 ? 1 : 0);
|
||||
}
|
||||
visible_map[row][col] = mcnt;
|
||||
}
|
||||
}
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int row = rid[i], col = cid[i];
|
||||
visible_status = visible_status * 10 + visible_map[row][col];
|
||||
}
|
||||
inner_to_visible[status] = visible_status;
|
||||
visible_to_inner[visible_status].push_back(status);
|
||||
}
|
||||
}
|
||||
const int buf_size = 4412555 * 4;
|
||||
unsigned char buf[buf_size], buf2[buf_size], buf3[buf_size], buf4[buf_size];
|
||||
int bcnt = 0;
|
||||
void CalculateProbability() {
|
||||
const LL raw_line_base = 243;
|
||||
const LL vis_line_base = 100000;
|
||||
already_have.clear();
|
||||
for (int status = 0; status < 14348907; status++) {
|
||||
int inner_mp[3][5] = {0}, visible_map[3][5];
|
||||
LL status_tmp = status;
|
||||
LL inverse_status =
|
||||
(status % raw_line_base) * raw_line_base * raw_line_base +
|
||||
((status / raw_line_base) % raw_line_base) * raw_line_base +
|
||||
(status / (raw_line_base * raw_line_base));
|
||||
if (already_have.find(inverse_status) != already_have.end()) continue;
|
||||
// assert(already_have.find(status) == already_have.end());
|
||||
already_have.insert(status);
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int row = rid[i], col = cid[i];
|
||||
inner_mp[row][col] = (status_tmp % 3); // uncode the inner_status
|
||||
status_tmp /= 3;
|
||||
}
|
||||
for (int row = 0; row < 3; row++)
|
||||
for (int col = 0; col < 5; col++) {
|
||||
if (inner_mp[row][col] == 0 || inner_mp[row][col] == 1) {
|
||||
visible_map[row][col] = 9; // 9 means unshown to player
|
||||
} else {
|
||||
int mcnt = 0;
|
||||
const int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1},
|
||||
dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int nr = row + dx[i], nc = col + dy[i];
|
||||
if (nr < 0 || nr >= 3 || nc < 0 || nc >= 5) continue;
|
||||
mcnt += (inner_mp[nr][nc] == 0 ? 1 : 0);
|
||||
}
|
||||
visible_map[row][col] = mcnt;
|
||||
}
|
||||
}
|
||||
LL visible_status = 0;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int row = rid[i], col = cid[i];
|
||||
visible_status = visible_status * 10 + visible_map[row][col];
|
||||
}
|
||||
int mine_cnt = 0;
|
||||
for(int i=0;i<visible_to_inner[visible_status].size();i++){
|
||||
mine_cnt += ((visible_to_inner[visible_status][i] / 729) % 3 == 0 ? 1 : 0);
|
||||
}
|
||||
visible_to_probability[visible_status] = double(mine_cnt) / visible_to_inner[visible_status].size();
|
||||
buf[bcnt++] = int((double(mine_cnt) / visible_to_inner[visible_status].size()) * 255);
|
||||
}
|
||||
// for (auto it = visible_to_inner.begin(); it != visible_to_inner.end(); ++it) {
|
||||
// assert(it->second.size() > 0);
|
||||
// int mine_cnt = 0;
|
||||
// for (int i = 0; i < it->second.size(); i++) {
|
||||
// mine_cnt += ((it->second[i] / 729) % 3 == 0 ? 1 : 0);
|
||||
// }
|
||||
// visible_to_probability[it->first] = double(mine_cnt) / it->second.size();
|
||||
// buf[bcnt++] = int((double(mine_cnt) / it->second.size()) * 255);
|
||||
// }
|
||||
}
|
||||
int main() {
|
||||
FindStatus();
|
||||
cout << visible_to_inner.size() << endl;
|
||||
CalculateProbability();
|
||||
freopen("tmp/data.txt", "w", stdout);
|
||||
string raw = base64_encode(buf, bcnt, false);
|
||||
cout << raw << endl;
|
||||
freopen("tmp/compressed.txt", "w", stdout);
|
||||
// buf is the raw data;
|
||||
// buf2 is the compressed data;
|
||||
// compressed is the base64-encoded form of buf2;
|
||||
// buf3 is the base64_decoded form of compressed,it should be the same as
|
||||
// buf2; buf4 is the decompressed data, it should be the same as buf;
|
||||
size_t real_size = compressData(buf, bcnt, buf2, buf_size);
|
||||
string compressed = base64_encode(buf2, real_size, false);
|
||||
cout << compressed << endl;
|
||||
// check the correctness of the compression and base64 encoding
|
||||
// freopen("tmp/decompressed.txt", "w", stdout);
|
||||
size_t real_size3 = base64_decode(compressed, buf3, buf_size, false);
|
||||
for (int i = 0; i < real_size3; i++) assert(buf3[i] == buf2[i]);
|
||||
size_t real_size4 = decompressData(buf3, real_size3, buf4, buf_size);
|
||||
for (int i = 0; i < real_size4; i++) assert(buf4[i] == buf[i]);
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user