SeaBattle海战游戏

SeaBattle海战游戏

fanz Lv3

规则

  • 游戏中两个玩家在两块完全相同的棋盘(10x10方格)上进行,两个玩家分别在各自的棋盘上放置他们的舰艇,当然对手是看不见的。
  • 每一个玩家都有5艘舰艇:一艘驱逐舰(2格),一艘潜艇(3格),一艘巡洋舰(3格),一艘战列舰(4格)和一艘航空母舰(5格)。
  • 每艘舰艇都在棋盘上占据一定数量的格子,每艘舰艇既可以横着放,也可以竖着放,但任意两艘舰艇不能互相交叠。
  • 游戏的玩法是双方轮流“轰炸”对方的舰艇,每次轰炸的结果是击中或未击中都会显示,如果击中,该玩家就可以继续攻击,直到击不中为止。
  • 游戏的目标是赶在对手之前把他所有的舰艇都击沉。

代码说明

代码文件

common.c 通用函数,打印信息

server.c 双方对战主函数。提供上次是否击中信息,轰炸

playera.c 玩家A舰艇部署和轰炸策略,需要自己修改,已给出例子

playerb.c 玩家B舰艇部署和轰炸策略,需要自己修改,已给出例子

舰艇说明

舰艇采用宏定义 见[[common.h]]文件

D为驱逐舰,S为潜艇,C为巡洋舰,B为战列舰,A为航母

部署后可以打印查看,如:

1
2
3
4
5
6
7
8
9
10
DD~~~~~~~~
SSS~~~~~~~
CCC~~~~~~~
BBBB~~~~~~
AAAAA~~~~~
~~~~~~~~~~
~~~~~~~~~~
~~~~~~~~~~
~~~~~~~~~~
~~~~~~~~~~

参与者可以任意选择玩家A或玩家B,分别修改playera.c和playerb.c的内容。

需要修改的地方:

  1. PlayerAName 或 PlayerBName 改为自己的名字。
  1. DeploymentA 或 DeploymentB 对舰艇进行部署。
  1. StrategyA 或 StrategyB 根据服务器提供的信息制定攻击策略,每次只能调用一次开火函数。

注意:player程序中只能使用server提供的ALastHitB、firetoA之类的函数,可以使用适当的数据结构记录历史的击中信息,但不能出现获取对方部署之类的函数,否则视为作弊,直接判负。

可以有两份策略,也可以使用完全相同的策略。

使用说明

除了player文件其他文件都不用修改。

自己编写好player代码,将所有文件加入到一个VC工程中,编译运行即可

先在本地调试自己的策略,然后将playera.c和playerb.c上传到自己的文件夹中。

也可以查看其他人的代码,与其他人进行对战。但不要针对其他人的部署进行攻击,因为实际中是不能看到对方部署的,而且要与所有人进行对战,每个人的部署都会不同。

每隔一定时间我会写脚本自动抓取每组的代码进行相互对战,胜率最高组有一定奖励!!!

玩得愉快!

后续变种:

  • 舰艇不能接触
  • 舰艇可对角线排放
  • 未击沉舰艇每回合后可移动(移动距离与其剩余格数一致)
  • 每个玩家都有一发特殊炮弹,可以击打2x2区域或1x4区域。
  • 增加计时功能,策略时间超过一定值直接忽略。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "player.h"
#include "server.h"

int initial = 0;
int researchButton = 1;

typedef struct Informations {
/* data */
int hitNum;
int optionS;
int optionH;
int last_Hit_Index[2];
int found[10][10];

} Information;

Battlefield PlayerA_BF;
char* PlayerAName = "fanzheng";
static int Weight[15][15];
// 玩家部署舰艇
int DeploymentA() {
for (int i = 0; i < M; i++)
for (int j = 0; j < M; j++) {
PlayerA_BF.area[i][j] = 1;
}
for (int i = 0; i < 15; i++)
for (int j = 0; j < 15; j++) {
Weight[i][j] = 0;
}
// add your arrangement, for example:
PlayerA_BF.area[0][0] = D;
PlayerA_BF.area[0][1] = D;
PlayerA_BF.area[2][2] = S;
PlayerA_BF.area[2][3] = S;
PlayerA_BF.area[2][4] = S;
PlayerA_BF.area[4][4] = C;
PlayerA_BF.area[4][5] = C;
PlayerA_BF.area[4][6] = C;
PlayerA_BF.area[6][5] = B;
PlayerA_BF.area[6][6] = B;
PlayerA_BF.area[6][7] = B;
PlayerA_BF.area[6][8] = B;
PlayerA_BF.area[8][2] = A;
PlayerA_BF.area[8][3] = A;
PlayerA_BF.area[8][4] = A;
PlayerA_BF.area[8][5] = A;
PlayerA_BF.area[8][6] = A;

printBF(&PlayerA_BF);
return 0;
}
int* getAttack_Object_Index() {
int* attack;
return attack;
}
static int lasti = 0;
static int lastj = 0;
static int Weight[15][15];
static int flag = 0;
// 根据上次是否击中确定策略,选择下一个攻击点
// 可以自己创建数据结构记录之前的击中与否信息
int StrategyA() {
int i = 0, j = 0, max;
// 从服务器端获取上次攻击是否击中信息
int isHit = ALastHitB();
if (flag != 0) {
Weight[lasti][lastj] -= 10000;
if (isHit == 1) {
// 打过的点权值减到底,击中的话给上下左右的点增加权重

if (lasti + 1 < 10) Weight[lasti + 1][lastj] += 5;
if (lasti - 1 >= 0) Weight[lasti - 1][lastj] += 5;
if (lastj + 1 < 10) Weight[lasti][lastj + 1] += 5;
if (lastj - 1 >= 0) Weight[lasti][lastj - 1] += 5;
if (lasti + 2 < 10) Weight[lasti + 2][lastj] += 4;
if (lasti - 2 >= 0) Weight[lasti - 2][lastj] += 4;
if (lastj + 2 < 10) Weight[lasti][lastj + 2] += 4;
if (lastj - 2 >= 0) Weight[lasti][lastj - 2] += 4;
if (lasti + 3 < 10) Weight[lasti + 3][lastj] += 3;
if (lasti - 3 >= 0) Weight[lasti - 3][lastj] += 3;
if (lastj + 3 < 10) Weight[lasti][lastj + 3] += 3;
if (lastj - 3 >= 0) Weight[lasti][lastj - 3] += 3;
if (lasti + 4 < 10) Weight[lasti + 4][lastj] += 2;
if (lasti - 4 >= 0) Weight[lasti - 4][lastj] += 2;
if (lastj + 4 < 10) Weight[lasti][lastj + 4] += 2;
if (lastj - 4 >= 0) Weight[lasti][lastj - 4] += 2;

}

else {
// 打过的点权值减到底,没击中的话给上下左右的点减少权重
Weight[lasti][lastj] -= 10000;
if (lasti + 1 < 10) Weight[lasti + 1][lastj] -= 5;
if (lasti - 1 >= 0) Weight[lasti - 1][lastj] -= 5;
if (lastj + 1 < 10) Weight[lasti][lastj + 1] -= 5;
if (lastj - 1 >= 0) Weight[lasti][lastj - 1] -= 5;
if (lasti + 2 < 10) Weight[lasti + 2][lastj] -= 4;
if (lasti - 2 >= 0) Weight[lasti - 2][lastj] -= 4;
if (lastj + 2 < 10) Weight[lasti][lastj + 2] -= 4;
if (lastj - 2 >= 0) Weight[lasti][lastj - 2] -= 4;
if (lasti + 3 < 10) Weight[lasti + 3][lastj] -= 3;
if (lasti - 3 >= 0) Weight[lasti - 3][lastj] -= 3;
if (lastj + 3 < 10) Weight[lasti][lastj + 3] -= 3;
if (lastj - 3 >= 0) Weight[lasti][lastj - 3] -= 3;
if (lasti + 4 < 10) Weight[lasti + 4][lastj] -= 2;
if (lasti - 4 >= 0) Weight[lasti - 4][lastj] -= 2;
if (lastj + 4 < 10) Weight[lasti][lastj + 4] -= 2;
if (lastj - 4 >= 0) Weight[lasti][lastj - 4] -= 2;
}
}

// 选择权重最大的点开火
for (int k = 0; k < 10; k++) {
for (int l = 0; l < 10; l++) {
if (k == 0 && l == 0) {
max = Weight[k][l];
}
if (Weight[k][l] > max) {
max = Weight[k][l];
i = k;
j = l;
}
}
}
// 向(i,j)位置开火
firetoB(i, j);
flag = 1;
lasti = i;
lastj = j;

return 0;
}

image.png

目前这个算法的缺点在于贪心的范围过广,很多时候没有意义。其实,每次打中之后都应该更关注于离集中点最近的区域,也即击中点的四周。如果某个击中点的四周都没有再次击中,就要考虑换位置了。

但是目前这个算法非常依赖于一次击中,然后再根据击中的区域逐步蔓延,可是这样的话,如果用户放的位置非常稀疏,这样可能会需要遍历整个数组才能找到所有炮弹。

目前最成功也最稳定的方法,贪心权值+破坏中心区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include "player.h"
#include "server.h"

int initial = 0;
int researchButton = 1;

typedef struct Informations {
/* data */
int hitNum;
int optionS;
int optionH;
int last_Hit_Index[2];
int found[10][10];
} Information;

Battlefield PlayerA_BF;
char* PlayerAName = "fanzheng";
static int Weight[15][15];
static int fanz_Step_Memory[15][15];

// 玩家部署舰艇
int DeploymentA() {
for (int i = 0; i < M; i++)
for (int j = 0; j < M; j++) {
PlayerA_BF.area[i][j] = 1;
}
for (int i = 0; i < 15; i++)
for (int j = 0; j < 15; j++) {
Weight[i][j] = 0;
fanz_Step_Memory[i][j]=0;
}

for(int j=4;j>=0;j--){
Weight[4-j][j]=3;
}
for(int j=9;j>=0;j--){
Weight[9-j][j]=3;
}
for(int j=5;j<10;j++){
Weight[14-j][j]=3;
}

// add your arrangement, for example:
PlayerA_BF.area[0][0] = D;
PlayerA_BF.area[0][1] = D;
PlayerA_BF.area[2][2] = S;
PlayerA_BF.area[2][3] = S;
PlayerA_BF.area[2][4] = S;
PlayerA_BF.area[4][4] = C;
PlayerA_BF.area[4][5] = C;
PlayerA_BF.area[4][6] = C;
PlayerA_BF.area[6][5] = B;
PlayerA_BF.area[6][6] = B;
PlayerA_BF.area[6][7] = B;
PlayerA_BF.area[6][8] = B;
PlayerA_BF.area[8][2] = A;
PlayerA_BF.area[8][3] = A;
PlayerA_BF.area[8][4] = A;
PlayerA_BF.area[8][5] = A;
PlayerA_BF.area[8][6] = A;

printBF(&PlayerA_BF);
return 0;

}

int* getAttack_Object_Index() {
int* attack;
return attack;
}

static int lasti = 0;
static int lastj = 0;
static int Weight[15][15];
static int flag = 0;
static int wrong_num=-10;

// 根据上次是否击中确定策略,选择下一个攻击点
// 可以自己创建数据结构记录之前的击中与否信息
int StrategyA() {
int i = 0, j = 0, max;
// 从服务器端获取上次攻击是否击中信息
int isHit = ALastHitB();
if (flag != 0) {
if (isHit == 1) {
fanz_Step_Memory[lasti][lastj]=1;
// 这里如果击中点的四周也有击中点,就可以知道方向了
// 水平方向
if((lastj>0&&fanz_Step_Memory[lasti][lastj-1]==1)||(lastj<10&&fanz_Step_Memory[lasti][lastj+1]==1)){
wrong_num==2;
int cur=lastj;
while (cur>=0)
{
if(fanz_Step_Memory[lasti][cur]==0)break;
cur--;
}
if(cur>=0) Weight[lasti][cur]+=10;
cur=lastj;
while (cur<10)
{
if(fanz_Step_Memory[lasti][cur]==0) break;
cur++;
}
if(cur<10) Weight[lasti][cur]+=10;
}

// 竖直方向
else if((lasti>0&&fanz_Step_Memory[lasti-1][lastj]==1)||(lasti<10&&fanz_Step_Memory[lasti+1][lastj]==1)){
wrong_num=2;
int cur=lasti;
while (cur>=0)
{
if(fanz_Step_Memory[cur][lastj]==0)break;
cur--;
}
if(cur>=0) Weight[cur][lastj]+=10;
cur=lasti;
while (cur<10)
{
if(fanz_Step_Memory[cur][lastj]==0) break;
cur++;
}
if(cur<10) Weight[cur][lastj]+=10;
}

else{
// 打过的点权值减到底,击中的话给上下左右的点增加权重
if (lasti + 1 < 10) Weight[lasti + 1][lastj] += 5;
if (lasti - 1 >= 0) Weight[lasti - 1][lastj] += 5;
if (lastj + 1 < 10) Weight[lasti][lastj + 1] += 5;
if (lastj - 1 >= 0) Weight[lasti][lastj - 1] += 5;
}
}

else {
// 打过的点权值减到底,没击中的话给上下左右的点减少权重
fanz_Step_Memory[lasti][lastj]=-1;
wrong_num--;
if (lasti + 1 < 10) Weight[lasti + 1][lastj] -= 5;
if (lasti - 1 >= 0) Weight[lasti - 1][lastj] -= 5;
if (lastj + 1 < 10) Weight[lasti][lastj + 1] -= 5;
if (lastj - 1 >= 0) Weight[lasti][lastj - 1] -= 5;
}
}

if(wrong_num==0){
destoryCenter();
}
// 选择权重最大的点开火
max=-200;
for (int k = 0; k < 10; k++) {
for (int l = 0; l < 10; l++) {
if(fanz_Step_Memory[k][l]==0){
if (Weight[k][l] > max) {
max = Weight[k][l];
i = k;
j = l;
}
}
}
}
// 向(i,j)位置开火
firetoB(i, j);
flag = 1;
lasti = i;
lastj = j;

return 0;
}




void destoryCenter(){
// 打破每行连续安全区域的中间位置
for(int i=0;i<10;i++){
int start=0;
for(;start<10;start++){
if(fanz_Step_Memory[i][start]==0){
int cur=start;
while (cur<10)
{
if(fanz_Step_Memory[i][cur]!=0) break;
cur++;
}
Weight[i][(start+cur-1)>>1]+=4;
start=cur;
}
}
}

// 打破每列连续安全区域的中间位置
for(int i=0;i<10;i++){
int start=0;
for(;start<10;start++){
if(fanz_Step_Memory[start][i]==0){
int cur=start;
while (cur<10)
{
if(fanz_Step_Memory[cur][i]!=0) break;
cur++;
}

Weight[(start+cur-1)>>1][i]+=4;
start=cur;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PlayerA_BF.area[4][4] = D;
PlayerA_BF.area[4][5] = D;
PlayerA_BF.area[9][0] = S;
PlayerA_BF.area[9][1] = S;
PlayerA_BF.area[9][2] = S;
PlayerA_BF.area[0][7] = C;
PlayerA_BF.area[0][8] = C;
PlayerA_BF.area[0][9] = C;
PlayerA_BF.area[0][0] = B;
PlayerA_BF.area[1][0] = B;
PlayerA_BF.area[2][0] = B;
PlayerA_BF.area[3][0] = B;
PlayerA_BF.area[5][9] = A;
PlayerA_BF.area[6][9] = A;
PlayerA_BF.area[7][9] = A;
PlayerA_BF.area[8][9] = A;
PlayerA_BF.area[9][9] = A;
  • 标题: SeaBattle海战游戏
  • 作者: fanz
  • 创建于 : 2024-11-25 17:44:01
  • 更新于 : 2025-02-17 22:47:29
  • 链接: https://redefine.ohevan.com/sni31d/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。