-
Notifications
You must be signed in to change notification settings - Fork 0
/
genmove.go
463 lines (418 loc) · 15.8 KB
/
genmove.go
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
package take11
import (
l03 "github.com/muzudho/kifuwarabe-wcsc31/lesson03"
l07 "github.com/muzudho/kifuwarabe-wcsc31/take7"
)
// マス番号が正常値でなければ強制終了させます
func ValidateSq(sq l03.Square) {
if !OnBoard(sq) && !OnHands(sq) {
panic(App.LogNotEcho.Fatal("TestSq: sq=%d", sq))
}
}
// GenMoveEnd - 利いているマスの一覧を返します。動けるマスではありません。
func GenMoveEnd(pPos *Position, from l03.Square) []MoveEnd {
moveEndList := []MoveEnd{}
if from == l03.SQ_EMPTY {
panic(App.LogNotEcho.Fatal("GenMoveEnd has empty square"))
} else if OnHands(from) {
// どこに打てるか
var start_rank l03.Square
var end_rank l03.Square
switch from {
case l03.SQ_R1, l03.SQ_B1, l03.SQ_G1, l03.SQ_S1, l03.SQ_R2, l03.SQ_B2, l03.SQ_G2, l03.SQ_S2: // 81マスに打てる
start_rank = 1
end_rank = 10
case l03.SQ_N1: // 3~9段目に打てる
start_rank = 3
end_rank = 10
case l03.SQ_L1, l03.SQ_P1: // 2~9段目に打てる
start_rank = 2
end_rank = 10
case l03.SQ_N2: // 1~7段目に打てる
start_rank = 1
end_rank = 8
case l03.SQ_L2, l03.SQ_P2: // 1~8段目に打てる
start_rank = 1
end_rank = 9
default:
panic(App.LogNotEcho.Fatal("unknown hand from=%d", from))
}
switch from {
case l03.SQ_P1:
// TODO 打ち歩詰め禁止
for rank := l03.Square(start_rank); rank < end_rank; rank += 1 {
for file := l03.Square(9); file > 0; file-- {
if !NifuFirst(pPos, file) { // 2歩禁止
sq := SquareFrom(file, rank)
ValidateSq(sq)
moveEndList = append(moveEndList, NewMoveEnd(sq, false))
}
}
}
case l03.SQ_P2:
// TODO 打ち歩詰め禁止
for rank := l03.Square(start_rank); rank < end_rank; rank += 1 {
for file := l03.Square(9); file > 0; file-- {
if !NifuSecond(pPos, file) { // 2歩禁止
sq := SquareFrom(file, rank)
ValidateSq(sq)
moveEndList = append(moveEndList, NewMoveEnd(sq, false))
}
}
}
default:
for rank := l03.Square(start_rank); rank < end_rank; rank += 1 {
for file := l03.Square(9); file > 0; file-- {
sq := SquareFrom(file, rank)
ValidateSq(sq)
moveEndList = append(moveEndList, NewMoveEnd(sq, false))
}
}
}
} else {
// 盤上の駒の利き
piece := pPos.Board[from]
// 2つ先のマスから斜めに長い利き
switch piece {
case l03.PIECE_B1, l03.PIECE_PB1, l03.PIECE_B2, l03.PIECE_PB2:
if l03.File(from) < 8 && l03.Rank(from) > 2 && pPos.IsEmptySq(from+9) { // 8~9筋にある駒でもなく、1~2段目でもなく、1つ左上が空マスなら
for to := from + 18; l03.File(to) != 0 && l03.Rank(to) != 0; to += 9 { // 2つ左上から
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
if l03.File(from) > 2 && l03.Rank(from) > 2 && pPos.IsEmptySq(from-11) { // 1~2筋にある駒でもなく、1~2段目でもなく、1つ右上が空マスなら
for to := from - 22; l03.File(to) != 0 && l03.Rank(to) != 0; to -= 11 { // 2つ右上から
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
if l03.File(from) < 8 && l03.Rank(from) < 8 && pPos.IsEmptySq(from+11) { // 8~9筋にある駒でもなく、8~9段目でもなく、1つ左下が空マスなら
for to := from + 22; l03.File(to) != 0 && l03.Rank(to) != 0; to += 11 { // 2つ左下から
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
if l03.File(from) > 2 && l03.Rank(from) < 8 && pPos.IsEmptySq(from-9) { // 1~2筋にある駒でもなく、8~9段目でもなく、1つ右下が空マスなら
for to := from - 18; l03.File(to) != 0 && l03.Rank(to) != 0; to -= 9 { // 2つ右下から
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
default:
// Ignored
}
// 2つ先のマスから先手香車の長い利き
switch piece {
case l03.PIECE_L1, l03.PIECE_R1, l03.PIECE_PR1, l03.PIECE_R2, l03.PIECE_PR2:
if l03.Rank(from) > 2 && pPos.IsEmptySq(from-1) { // 1~2段目にある駒でもなく、1つ上が空マスなら
for to := from - 2; l03.Rank(to) != 0; to -= 1 { // 上
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
default:
// Ignored
}
// 2つ先のマスから後手香車の長い利き
switch piece {
case l03.PIECE_R1, l03.PIECE_PR1, l03.PIECE_L2, l03.PIECE_R2, l03.PIECE_PR2:
if l03.Rank(from) < 8 && pPos.IsEmptySq(from+1) { // 8~9段目にある駒でもなく、1つ下が空マスなら
for to := from + 2; l03.Rank(to) != 0; to += 1 { // 下
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
default:
// Ignored
}
// 2つ横のマスから飛の長い利き
switch piece {
case l03.PIECE_R1, l03.PIECE_PR1, l03.PIECE_R2, l03.PIECE_PR2:
if l03.File(from) < 8 && pPos.IsEmptySq(from+10) { // 8~9筋にある駒でもなく、1つ左が空マスなら
for to := from + 20; l03.File(to) != 0; to += 10 { // 左
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
if l03.File(from) > 2 && pPos.IsEmptySq(from-10) { // 1~2筋にある駒でもなく、1つ右が空マスなら
for to := from - 20; l03.File(to) != 0; to -= 10 { // 右
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
if !pPos.IsEmptySq(to) {
break
}
}
}
default:
// Ignored
}
// 先手桂の利き
if piece == l03.PIECE_N1 && 2 < l03.Rank(from) && l03.Rank(from) < 10 {
if 0 < l03.File(from) && l03.File(from) < 9 { // 左上桂馬飛び
to := from + 8
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
if 1 < l03.File(from) && l03.File(from) < 10 { // 右上桂馬飛び
to := from - 12
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
}
// 後手桂の利き
if piece == l03.PIECE_N2 {
if to := from + 12; l03.File(to) != 0 && l03.Rank(to) != 0 && l03.Rank(to) != 9 { // 左下
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
if to := from - 8; l03.File(to) != 0 && l03.Rank(to) != 0 && l03.Rank(to) != 9 { // 右下
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
}
// 先手歩の利き
switch piece {
case l03.PIECE_K1, l03.PIECE_R1, l03.PIECE_PR1, l03.PIECE_PB1, l03.PIECE_G1, l03.PIECE_S1, l03.PIECE_L1, l03.PIECE_P1, l03.PIECE_PS1,
l03.PIECE_PN1, l03.PIECE_PL1, l03.PIECE_PP1, l03.PIECE_K2, l03.PIECE_R2, l03.PIECE_PR2, l03.PIECE_PB2, l03.PIECE_G2, l03.PIECE_PS2,
l03.PIECE_PN2, l03.PIECE_PL2, l03.PIECE_PP2:
if to := from - 1; l03.Rank(to) != 0 { // 上
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
default:
// Ignored
}
// 後手歩の利き
switch piece {
case l03.PIECE_K2, l03.PIECE_R2, l03.PIECE_PR2, l03.PIECE_PB2, l03.PIECE_G2, l03.PIECE_S2, l03.PIECE_L2, l03.PIECE_P2, l03.PIECE_PS2,
l03.PIECE_PN2, l03.PIECE_PL2, l03.PIECE_PP2, l03.PIECE_K1, l03.PIECE_R1, l03.PIECE_PR1, l03.PIECE_PB1, l03.PIECE_G1, l03.PIECE_PS1,
l03.PIECE_PN1, l03.PIECE_PL1, l03.PIECE_PP1:
if to := from + 1; l03.Rank(to) != 0 { // 下
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
default:
// Ignored
}
// 先手斜め前の利き
switch piece {
case l03.PIECE_K1, l03.PIECE_PR1, l03.PIECE_B1, l03.PIECE_PB1, l03.PIECE_G1, l03.PIECE_S1, l03.PIECE_PS1, l03.PIECE_PN1, l03.PIECE_PL1,
l03.PIECE_PP1, l03.PIECE_K2, l03.PIECE_PR2, l03.PIECE_B2, l03.PIECE_PB2, l03.PIECE_S2:
if to := from + 9; l03.File(to) != 0 && l03.Rank(to) != 0 { // 左上
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
if to := from - 11; l03.File(to) != 0 && l03.Rank(to) != 0 { // 右上
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
default:
// Ignored
}
// 後手斜め前の利き
switch piece {
case l03.PIECE_K2, l03.PIECE_PR2, l03.PIECE_B2, l03.PIECE_PB2, l03.PIECE_G2, l03.PIECE_S2, l03.PIECE_PS2, l03.PIECE_PN2, l03.PIECE_PL2,
l03.PIECE_PP2, l03.PIECE_K1, l03.PIECE_PR1, l03.PIECE_B1, l03.PIECE_PB1, l03.PIECE_S1:
if to := from + 11; l03.File(to) != 0 && l03.Rank(to) != 0 { // 左下
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
if to := from - 9; l03.File(to) != 0 && l03.Rank(to) != 0 { // 右下
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
default:
// Ignored
}
// 横1マスの利き
switch piece {
case l03.PIECE_K1, l03.PIECE_R1, l03.PIECE_PR1, l03.PIECE_PB1, l03.PIECE_G1, l03.PIECE_PS1, l03.PIECE_PN1, l03.PIECE_PL1, l03.PIECE_PP1,
l03.PIECE_K2, l03.PIECE_R2, l03.PIECE_PR2, l03.PIECE_PB2, l03.PIECE_G2, l03.PIECE_PS2, l03.PIECE_PN2, l03.PIECE_PL2, l03.PIECE_PP2:
if to := from + 10; l03.File(to) != 0 { // 左
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
if to := from - 10; l03.File(to) != 0 { // 右
ValidateSq(to)
moveEndList = append(moveEndList, NewMoveEnd(to, false))
}
default:
// Ignored
}
}
return moveEndList
}
// NifuFirst - 先手で二歩になるか筋調べ
func NifuFirst(pPos *Position, file l03.Square) bool {
for rank := l03.Square(2); rank < 10; rank += 1 {
if pPos.Board[SquareFrom(file, rank)] == l03.PIECE_P1 {
return true
}
}
return false
}
// NifuSecond - 後手で二歩になるか筋調べ
func NifuSecond(pPos *Position, file l03.Square) bool {
for rank := l03.Square(1); rank < 9; rank += 1 {
if pPos.Board[SquareFrom(file, rank)] == l03.PIECE_P2 {
return true
}
}
return false
}
// GenMoveList - 現局面の指し手のリスト。合法手とは限らないし、全ての合法手を含むとも限らないぜ(^~^)
func GenMoveList(pPosSys *PositionSystem, pPos *Position) []l03.Move {
move_list := []l03.Move{}
// 王手をされているときは、自玉を逃がす必要があります
friend := pPosSys.GetPhase()
opponent := FlipPhase(pPosSys.GetPhase())
var friendKingSq l03.Square
var hand_start l03.HandIdx
var hand_end l03.HandIdx
// var opponentKingSq l03.Square
if friend == l03.FIRST {
friendKingSq = pPos.GetPieceLocation(l07.PCLOC_K1)
hand_start = l03.HAND_IDX_BEGIN
} else if friend == l03.SECOND {
friendKingSq = pPos.GetPieceLocation(l07.PCLOC_K2)
hand_start = l03.HAND_IDX_BEGIN + l03.HAND_TYPE_SIZE
} else {
panic(App.LogNotEcho.Fatal("unknown phase=%d", friend))
}
hand_end = hand_start + l03.HAND_TYPE_SIZE
// 相手の利きテーブルの自玉のマスに利きがあるか
if pPosSys.ControlBoards[opponent-1][CONTROL_LAYER_SUM][friendKingSq] > 0 {
// 王手されています
// fmt.Printf("Debug: Checked friendKingSq=%d opponentKingSq=%d friend=%d opponent=%d\n", friendKingSq, opponentKingSq, friend, opponent)
// TODO アタッカーがどの駒か調べたいが……。一手前に動かした駒か、空き王手のどちらかしかなくないか(^~^)?
// 王手されているところが開始局面だと、一手前を調べることができないので、やっぱ調べるしか(^~^)
// 空き王手を利用して、2箇所から 長い利きが飛んでくることはある(^~^)
// 盤上の駒を動かしてみて、王手が解除されるか調べるか(^~^)
for rank := 1; rank < 10; rank += 1 {
for file := 1; file < 10; file += 1 {
from := l03.Square(file*10 + rank)
if pPos.Homo(from, friendKingSq) { // 自玉と同じプレイヤーの駒を動かします
moveEndList := GenMoveEnd(pPos, from)
piece := pPos.Board[from]
pieceType := l03.What(piece)
if pieceType == l03.PIECE_TYPE_K {
// 玉は自殺手を省きます
for _, moveEnd := range moveEndList {
to, pro := moveEnd.Destructure()
// 敵の長い駒の利きは、玉が逃げても伸びてくる方向があるので、
// いったん玉を動かしてから 再チェックするぜ(^~^)
if pPos.Hetero(from, to) { // 自駒の上には移動できません
move := l03.NewMove(from, to, pro)
pPosSys.DoMove(pPos, move)
if pPosSys.ControlBoards[opponent-1][CONTROL_LAYER_SUM][to] == 0 {
// よっしゃ利きから逃げ切った(^~^)
// 王手が解除されてるから採用(^~^)
move_list = append(move_list, move)
}
pPosSys.UndoMove(pPos)
}
}
} else {
for _, moveEnd := range moveEndList {
to, pro := moveEnd.Destructure()
if pPos.Hetero(from, to) { // 自駒の上には移動できません
move := l03.NewMove(from, to, pro)
pPosSys.DoMove(pPos, move)
if pPosSys.ControlBoards[opponent-1][CONTROL_LAYER_SUM][friendKingSq] == 0 {
// 王手が解除されてるから採用(^~^)
move_list = append(move_list, move)
}
pPosSys.UndoMove(pPos)
}
}
}
}
}
}
// 自分の駒台もスキャンしよ(^~^)
for hand_index := hand_start; hand_index < hand_end; hand_index += 1 {
if pPos.Hands1[hand_index] > 0 {
hand_sq := l03.Square(hand_index) + l03.SQ_HAND_START
moveEndList := GenMoveEnd(pPos, hand_sq)
for _, moveEnd := range moveEndList {
to, pro := moveEnd.Destructure()
if pPos.IsEmptySq(to) { // 駒の上には打てません
move := l03.NewMove(hand_sq, to, pro)
pPosSys.DoMove(pPos, move)
if pPosSys.ControlBoards[opponent-1][CONTROL_LAYER_SUM][friendKingSq] == 0 {
// 王手が解除されてるから採用(^~^)
move_list = append(move_list, move)
}
pPosSys.UndoMove(pPos)
}
}
}
}
} else {
// 王手されていないぜ(^~^)
// fmt.Printf("Debug: Not checked\n")
// 盤面スキャンしたくないけど、駒の位置インデックスを作ってないから 仕方ない(^~^)
for rank := 1; rank < 10; rank += 1 {
for file := 1; file < 10; file += 1 {
from := l03.Square(file*10 + rank)
if pPos.Homo(from, friendKingSq) { // 自玉と同じプレイヤーの駒を動かします
moveEndList := GenMoveEnd(pPos, from)
piece := pPos.Board[from]
pieceType := l03.What(piece)
if pieceType == l03.PIECE_TYPE_K {
// 玉は自殺手を省きます
for _, moveEnd := range moveEndList {
to, pro := moveEnd.Destructure()
if pPos.Hetero(from, to) && pPosSys.ControlBoards[opponent-1][CONTROL_LAYER_SUM][to] == 0 { // 自駒の上、敵の利きには移動できません
move_list = append(move_list, l03.NewMove(from, to, pro))
}
}
} else {
for _, moveEnd := range moveEndList {
to, pro := moveEnd.Destructure()
if pPos.Hetero(from, to) { // 自駒の上には移動できません
move_list = append(move_list, l03.NewMove(from, to, pro))
}
}
}
}
}
}
// 自分の駒台もスキャンしよ(^~^)
for hand_index := hand_start; hand_index < hand_end; hand_index += 1 {
if pPos.Hands1[hand_index] > 0 {
hand_sq := l03.Square(hand_index) + l03.SQ_HAND_START
moveEndList := GenMoveEnd(pPos, hand_sq)
for _, moveEnd := range moveEndList {
to, pro := moveEnd.Destructure()
if pPos.IsEmptySq(to) { // 駒の上には打てません
move_list = append(move_list, l03.NewMove(hand_sq, to, pro))
}
}
}
}
}
return move_list
}