-
Notifications
You must be signed in to change notification settings - Fork 1
/
minesweeper_kanno.cpp
357 lines (304 loc) · 8.82 KB
/
minesweeper_kanno.cpp
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* 碁盤の縦横 */
#define ROW 12
#define COL 16
/* debug (関数名と任意のメッセージを表示します.printf()関数と同様の使い方) */
/*C99
#define DEBUG(...) DEBUG_2(__VA_ARGS__, "")
#define DEBUG_2(fmt, ...) \
printf("%s(): "fmt"%s", __func__, __VA_ARGS__)
*/
//GNU Only
//#define DEBUG(fmt, ...) \
// printf("%s(): "fmt, __func__, ## __VA_ARGS__)
char printGo(int num);
void printField(int open[][COL], int number[][COL],int field[][COL],int flag);
void bomb( int field[][COL], int doukasen);
int fieldCheck(int open[][COL], int row, int col);
void fieldChange(int open[][COL], int number[][COL], int row, int col);
int win(int field[][COL], int open[][COL]);
void Msg(int code);
int input(int field[][COL], int open[][COL], int number[][COL]);
void defNumber(int field[][COL], int number[][COL]);
void game();
int main(void);
char printGo(int num)
{
char str[2]; // 一文字分の文字列
/* 碁盤のマスの数値を文字に変換します */
switch(num) {
case 0:
return ' ';
default:
break;
}
sprintf(str, "%d", num); // 数値から文字列に変換
return str[0];
}
void printField(int open[][COL], int number[][COL],int field[][COL],int flag)
{
/* 碁盤を表示します */
int i, j; // 繰り返しのため
printf("開いていない -> o, 周囲の爆弾数 -> 数字, フラグ -> f\n");
// 列番号の表示
printf(" ");
for(i = 0; i < COL; i++) {
printf("%2d ", i+1);
}
printf("\n");
// 列番号区切りの表示
printf(" ");
for(i = 0; i < COL; i++) {
printf("---");
}
printf("\n");
// フィールドの表示
for(i = 0; i < ROW; i++) {
// 行番号の表示
printf("%2d | ",i+1);
// マス目の表示
for(j = 0; j < COL; j++) {
if(field[i][j]== 1 && flag == 1){
printf(" b ");
continue;
}
if(open[i][j] == -1) {
printf(" f ");
continue;
}
else if(open[i][j] == 1) {
printf(" %c ", printGo(number[i][j]));
}
else {
printf(" o ");
}
}
// 行番号の表示
printf("| %2d\n",i+1);
}
// 列番号区切りの表示
printf(" ");
for(i = 0; i < COL; i++) {
printf("---");
}
printf("\n");
// 列番号の表示
printf(" ");
for(i = 0; i < COL; i++) {
printf("%2d ", i+1);
}
printf("\n");
return;
}
/* 爆弾位置を書き込みします(ランダムでやる) */
void bomb( int field[][COL],int doukasen)
{
int i, j, count=0;
// シード値
srand((unsigned)time(NULL));
while(count < doukasen) {
i = rand()%ROW;
j = rand()%COL;
if(field[i][j] == 1) continue;
field[i][j] = 1;
count++;
}
/*
field[0][4] = 1;
field[1][3] = 1;
field[3][9] = 1;
field[6][8] = 1;
field[9][0] = 1;
field[7][5] = 1;
field[3][8] = 1;
*/
return;
}
int fieldCheck(int open[][COL], int row, int col)
{
/* 入力した指し手が不正な数値でないか確認します */
/* 入力値が範囲外でないか */
if(row < 0 || ROW <= row) return -1;
if(col < 0 || COL <= col) return -2;
/* 既に穴が開いていないか */
if(open[row][col] > 0) return 1;
return 0;
}
void fieldChange(int open[][COL], int number[][COL], int row, int col)
{
/* マインスイーパの配列に適切な穴を開けます *
* クイックソートのような再帰的なやり方です *
*/
/* 自分自身が穴なら終了 */
if(open[row][col] == 1) return;
else if(number[row][col] > 0) {
open[row][col] = 1;
return;
}
else open[row][col] = 1;
/* 再帰!!!(ただし配列の範囲外にならないように) */
if(row-1 >= 0) fieldChange(open, number, row-1, col);
if(row+1 < ROW) fieldChange(open, number, row+1, col);
if(col-1 >= 0) fieldChange(open, number, row, col-1);
if(col+1 < COL) fieldChange(open, number, row, col+1);
return;
}
int win(int field[][COL], int open[][COL])
{
int i, j;
for(i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
/* 全ての要素に対して実行 */
/* 爆弾位置以外は穴が開いているか判定 */
if(field[i][j] != 1) {
if(open[i][j] == 0) return 1;
if(open[i][j] == -1) return 1;
}
}
}
return 0;
}
void Msg(int code)
{
/* メッセージを表示します */
if(code > 0) {
printf("このマスはすでに開いているか、フラグです。選択しなおしてください。\n");
}
else if(code < 0) {
printf("このマスは範囲外です。選択しなおしてください。\n");
}
else {
printf("選択が完了しました\n");
}
return;
}
int input(int field[][COL], int open[][COL], int number[][COL])
{
/* 指し手の入力を受付け,穴をあけます */
int row = 0; // ユーザー指定の位置
int col = 0; // ユーザー指定の位置
int mode; // モード保持
int status = 0; // 入力値が不正かどうかの結果を保存するため
while(1) {
printField(open, number,field,0); // 碁盤の表示
printf("モードを選択してください(入力後に Enter キーを押してください)\n");
printf("フラグを立てる・消す : 1, 穴を開ける : 2\n");
/* とりあえず文字入力時の無限ループ回避 */
if(scanf("%d", &mode) != 1) {
scanf("%*s");
if(feof(stdin)) printf("Buffer Error!\n");
printf("\n不正な値です.再入力してください.(r)\n\n");
continue;
}
/* 異なる文字の対策 */
if(!(mode == 1 || mode == 2)) {
printf("\n不正な値です.再入力してください.(mfo)\n\n");
continue;
}
printf("あなたのマス目を選択してください\n");
printf("縦(入力後に Enter キーを押してください) : ");
/* とりあえず文字入力時の無限ループ回避 */
if(scanf("%d", &row) != 1) {
scanf("%*s");
if(feof(stdin)) printf("Buffer Error!\n");
printf("\n不正な値です.再入力してください.(r)\n\n");
continue;
}
printf("横(入力後に Enter キーを押してください) : ");
/* とりあえず文字入力時の無限ループ回避 */
if(scanf("%d", &col) != 1) {
scanf("%*s");
if(feof(stdin)) printf("Buffer Error!\n");
printf("\n不正な値です.再入力してください.(c)\n\n");
continue;
}
/* 入力番号から配列番号へ変換 */
row--;
col--;
status = fieldCheck(open, row, col); // 入力値のチェック
Msg(status); // チェック結果のメッセージを表示
if(status == 0) break; // 正常ならループを抜ける
}
if(mode == 2) {
if(field[row][col] == 1) { // 敗北判定
printField(open, number,field,1); // 碁盤の表示
printf("you lose!\n");
return -1;
}
fieldChange(open, number, row, col); // フィールドに穴を開ける
if(win(field, open) == 0) { // 勝利判定
printField(open, number,field,1); // 碁盤の表示
printf("you win!\n");
return 1;
}
}
else if(mode == 1) {
/* フラグが立っていないなら、フラグを立てる */
if(open[row][col] == 0) open[row][col] = -1;
/* フラグが立っているなら、フラグを消す */
else if(open[row][col] == -1) open[row][col] = 0;
}
return 0;
}
/* マインスイーパの 1, 2, 3 のような数値の算出(コードがとても見にくい) */
void defNumber(int field[][COL], int number[][COL])
{
int i, j, num;
int iflug;
int jflug;
for(i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
/* 全ての要素に対して実行 */
num = 0;
/* 配列の要素が端にきているかどうかの判定 */
if(i == 0) iflug = -1;
else if(i == ROW - 1) iflug = 1;
else iflug = 0;
if(j == 0) jflug = -1;
else if(j == COL -1) jflug = 1;
else jflug = 0;
/* 自分の周りに爆弾が何個あるか判定。見にくい */
if(iflug != -1 && jflug != -1 && field[i-1][j-1] == 1) num++;
if(jflug != -1 && field[i][j-1] == 1) num++;
if(iflug != 1 && jflug != -1 && field[i+1][j-1] == 1) num++;
if(iflug != -1 && jflug != 1 && field[i-1][j+1] == 1) num++;
if(jflug != 1 && field[i][j+1] == 1) num++;
if(iflug != 1 && jflug != 1 && field[i+1][j+1] == 1) num++;
if(iflug != -1 && field[i-1][j] == 1) num++;
if(iflug != 1 && field[i+1][j] == 1) num++;
/* 自分の位置が爆弾なら 0 とし、そうでなければ爆弾数を代入 */
if(field[i][j] == 0) number[i][j] = num;
else number[i][j] = 0;
}
}
return;
}
void game()
{
/* 指し手を変更したり,入力させたり,判定をしたり... */
/* 初期化 */
int field[ROW][COL] = {}; // フィールド(何もない = 0; 爆弾位置 = 1;)
int open[ROW][COL] = {}; // マスがオープンされているか(フラグを立てた = -1, されてない = 0; されてる = 1;)
int number[ROW][COL] = {}; // マスに表示する数値(爆弾 = -1, 何も表示しない = 0; 表示する = 1 以上;)
int doukasen;
int status = 0;
printf("爆弾の数を決めてください\n");
scanf("%d",&doukasen);
bomb(field,doukasen); // 爆弾位置を決定
defNumber(field, number); // 爆弾位置に応じて、1, 2, 3 みたいな数値を算出
printf("マインスイーパを開始します。(端末サイズを80x24にしてください)。\n");
while(1) {
status = input(field, open, number); // 碁石の置く場所を入力,判定など
if(status != 0) break; // もし,勝敗がついたなら,ループから抜ける
}
scanf("%d",&status);
return;
}
int main(void)
{
/* ここが全ての始まり */
game();
return 0;
}