Skip to content

Commit f250a03

Browse files
improve promotion sorting
1 parent 869abe9 commit f250a03

File tree

8 files changed

+96
-58
lines changed

8 files changed

+96
-58
lines changed

attack.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
package main
2525

26+
import "fmt"
27+
2628
func attack_map(brd *Board, occ BB, sq int) BB {
2729
return ((pawn_attack_masks[BLACK][sq] & brd.pieces[WHITE][PAWN]) | // Pawns
2830
(pawn_attack_masks[WHITE][sq] & brd.pieces[BLACK][PAWN])) |
@@ -135,6 +137,7 @@ func get_see(brd *Board, from, to int, captured_piece Piece) int {
135137
// this move is illegal and will be discarded by search. return the lowest possible
136138
// SEE value so that this move will be put at end of list. If cutoff occurs before then,
137139
// the cost of detecting the illegal move will be saved.
140+
fmt.Println("king capture detected in get_see()!")
138141
return SEE_MIN
139142
}
140143
t = brd.TypeAt(from)

board.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,9 @@ func (brd *Board) EvadesCheck(m Move) bool {
122122
king_sq := brd.KingSq(c)
123123
threats := color_attack_map(brd, occ, king_sq, e, c)
124124

125-
// TODO: EvadesCheck() called from non-check position in rare cases. Example:
125+
// TODO: EvadesCheck() called from non-check position in rare cases. Examples:
126126
// 5r1k/1b3p1p/pp3p1q/3n4/1P2R3/P2B1PP1/7P/6K1 w - - 0 1
127+
// 8/PPKR4/1Bn4P/3P3R/8/2p4r/pp4p1/r6k w - - 5 2 (r h3h5 x r)...?
127128

128129
if threats == 0 {
129130
fmt.Println("EvadesCheck() called from non-check position!")

eval.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func evaluate(brd *Board, alpha, beta int) int {
202202
}
203203

204204
score += net_pawn_placement(brd, pentry, c, e)
205-
score += net_major_placement(brd, pentry, c, e)
205+
score += net_major_placement(brd, pentry, c, e) // 3x as expensive as pawn eval...
206206

207207
return score
208208
}

main.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ import (
2929
"fmt"
3030
"os"
3131
"runtime"
32+
"sync"
3233

3334
"github.com/pkg/profile"
3435
)
3536

37+
var setup_once sync.Once
38+
3639
func max(a, b int) int {
3740
if a > b {
3841
return a
@@ -62,19 +65,21 @@ func assert(statement bool, failure_message string) {
6265
}
6366

6467
func setup() {
65-
num_cpu := runtime.NumCPU()
66-
runtime.GOMAXPROCS(num_cpu)
67-
setup_chebyshev_distance()
68-
setup_masks()
69-
setup_magic_move_gen()
70-
setup_eval()
71-
setup_rand()
72-
setup_zobrist()
73-
reset_main_tt()
74-
setup_load_balancer(num_cpu)
68+
setup_once.Do(func() {
69+
num_cpu := runtime.NumCPU()
70+
runtime.GOMAXPROCS(num_cpu)
71+
setup_chebyshev_distance()
72+
setup_masks()
73+
setup_magic_move_gen()
74+
setup_eval()
75+
setup_rand()
76+
setup_zobrist()
77+
reset_main_tt()
78+
setup_load_balancer(num_cpu)
79+
})
7580
}
7681

77-
var version = "0.1.0"
82+
var version = "0.1.1"
7883

7984
func print_name() {
8085
fmt.Printf("\n---------------------------------------\n")
@@ -97,10 +102,11 @@ func main() {
97102
print_name()
98103
defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
99104
RunTestSuite("test_suites/wac_75.epd", MAX_DEPTH, 5000)
105+
// run 'go tool pprof -text gopher_check cpu.pprof > cpu_prof.txt' to output profile to text
100106
} else if *mem_profile_flag {
101107
print_name()
102108
defer profile.Start(profile.MemProfile, profile.ProfilePath(".")).Stop()
103-
RunTestSuite("test_suites/wac_75.epd", MAX_DEPTH, 5000)
109+
RunTestSuite("test_suites/wac_150.epd", MAX_DEPTH, 5000)
104110
} else {
105111
uci := NewUCIAdapter()
106112
uci.Read(bufio.NewReader(os.Stdin))

move_gen.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ func get_winning_captures(brd *Board, htable *HistoryTable, winning *MoveList) {
332332
for ; promotion_advances > 0; promotion_advances.Clear(to) {
333333
to = furthest_forward(c, promotion_advances)
334334
from = to + pawn_from_offsets[c][OFF_SINGLE]
335-
get_promotion_captures(brd, winning, from, to, EMPTY)
335+
get_promotion_advances(brd, winning, winning, from, to)
336336
}
337337

338338
// regular pawn attacks
@@ -824,8 +824,8 @@ func get_promotion_advances(brd *Board, winning, losing *MoveList, from, to int)
824824
var sort uint64
825825
for pc := Piece(QUEEN); pc >= KNIGHT; pc-- {
826826
m = NewMove(from, to, PAWN, EMPTY, pc)
827-
sort = SortPromotion(brd, m)
828-
if sort >= SORT_WINNING {
827+
sort = sort_promotion_advances(brd, from, to, pc)
828+
if sort >= SORT_WINNING_PROMOTION {
829829
winning.Push(&SortItem{m, sort})
830830
} else {
831831
losing.Push(&SortItem{m, sort})
@@ -837,6 +837,6 @@ func get_promotion_captures(brd *Board, winning *MoveList, from, to int, capture
837837
var m Move
838838
for pc := Piece(QUEEN); pc >= KNIGHT; pc-- {
839839
m = NewMove(from, to, PAWN, captured_piece, pc)
840-
winning.Push(&SortItem{m, SortPromotion(brd, m)})
840+
winning.Push(&SortItem{m, sort_promotion_captures(brd, from, to, captured_piece, pc)})
841841
}
842842
}

move_gen_test.go

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import (
3030
"time"
3131
)
3232

33-
var legal_max_tree = [10]int{1, 20, 400, 8902, 197281, 4865609, 119060324, 3195901860, 84998978956, 2439530234167}
33+
// var legal_max_tree = [10]int{1, 20, 400, 8902, 197281, 4865609, 119060324, 3195901860, 84998978956, 2439530234167}
34+
var legal_max_tree = [10]int{1, 24, 496, 9483, 182838, 3605103, 71179139}
3435

3536
// func TestLegalMoveGen(t *testing.T) {
3637
// legal_movegen(Perft)
@@ -43,7 +44,11 @@ var legal_max_tree = [10]int{1, 20, 400, 8902, 197281, 4865609, 119060324, 31959
4344
func legal_movegen(fn func(brd *Board, htable *HistoryTable, stk Stack, depth, ply int) int) {
4445
setup()
4546
htable := new(HistoryTable)
46-
brd := StartPos()
47+
// brd := StartPos()
48+
brd := ParseFENString("n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1")
49+
// brd := ParseFENString("5r1k/1b3p1p/pp3p1q/3n4/1P2R3/P2B1PP1/7P/6K1 w - - 0 1")
50+
51+
brd.Print()
4752
copy := brd.Copy()
4853
depth := 5
4954
start := time.Now()
@@ -54,39 +59,32 @@ func legal_movegen(fn func(brd *Board, htable *HistoryTable, stk Stack, depth, p
5459

5560
fmt.Printf("%d nodes at depth %d. %d NPS\n", sum, depth, nps)
5661

57-
fmt.Printf("%d total nodes in check\n", check_count)
58-
fmt.Printf("%d total capture nodes\n", capture_count)
59-
6062
CompareBoards(copy, brd)
6163
assert(*brd == *copy, "move generation did not return to initial board state.")
6264
assert(sum == legal_max_tree[depth], "Expected "+strconv.Itoa(legal_max_tree[depth])+
6365
" nodes, got "+strconv.Itoa(sum))
6466
}
6567

66-
var check_count int
67-
var capture_count int
68-
var parallel_count int
69-
7068
func Perft(brd *Board, htable *HistoryTable, stk Stack, depth, ply int) int {
71-
if depth == 0 {
72-
return 1
73-
}
7469
sum := 0
7570
in_check := brd.InCheck()
76-
if in_check {
77-
check_count += 1
78-
}
7971
this_stk := stk[ply]
8072
memento := brd.NewMemento()
8173
generator := NewMoveSelector(brd, &this_stk, htable, in_check, NO_MOVE)
74+
8275
for m, _ := generator.Next(SP_NONE); m != NO_MOVE; m, _ = generator.Next(SP_NONE) {
83-
if m.IsCapture() {
84-
capture_count += 1
76+
if depth > 1 {
77+
make_move(brd, m)
78+
sum += Perft(brd, htable, stk, depth-1, ply+1)
79+
// if depth-1 > 0 {
80+
// fmt.Printf("%s\n", m.ToUCI())
81+
// }
82+
unmake_move(brd, m, memento)
83+
} else {
84+
sum += 1
8585
}
86-
make_move(brd, m)
87-
sum += Perft(brd, htable, stk, depth-1, ply+1)
88-
unmake_move(brd, m, memento)
8986
}
87+
9088
return sum
9189
}
9290

search_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import "testing"
3939
func TestPlayingStrength(t *testing.T) {
4040
print_name()
4141
setup()
42-
4342
depth := MAX_DEPTH
4443
timeout := 2000
4544
RunTestSuite("test_suites/wac_300.epd", depth, timeout)

sort.go

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,26 @@
2323

2424
package main
2525

26-
import (
27-
// "fmt"
28-
"sort"
29-
)
26+
import
27+
// "fmt"
28+
29+
"sort"
3030

3131
// Root Sorting
3232
// At root, moves should be sorted based on subtree value rather than standard sorting.
3333

3434
// bit pos. (LSB order)
3535
// 28 Winning promotions (1 bits)
36+
// 1 <<padding>> (1 bit)
3637
// 22 MVV/LVA (6 bits) - Used to choose between captures of equal material gain/loss
3738
// 1 History heuristic : (21 bits)
3839
// 0 Castles (1 bit)
3940

4041
const (
41-
SORT_WINNING = (1 << 28)
42+
SORT_WINNING_PROMOTION = (1 << 31)
43+
SORT_LOSING_PROMOTION = (1 << 29)
4244
)
4345

44-
func mvv_lva(victim, attacker Piece) uint64 { // returns value between 0 and 64
45-
return uint64(((victim+1)<<3)-attacker) << 22
46-
}
47-
4846
// TODO: promotions not ordered correctly.
4947

5048
// Promotion Captures:
@@ -53,20 +51,47 @@ func mvv_lva(victim, attacker Piece) uint64 { // returns value between 0 and 64
5351
// Non-capture promotions:
5452
// if square undefended, gain is promote_values[promoted_piece].
5553
// If defended, gain is SEE score where captured_piece == EMPTY
56-
func SortPromotion(brd *Board, m Move) uint64 {
57-
var val int
58-
if is_attacked_by(brd, brd.AllOccupied()&sq_mask_off[m.From()], m.To(), brd.Enemy(), brd.c) {
59-
val = get_see(brd, m.From(), m.To(), m.CapturedPiece()) // defended
60-
} else {
61-
val = m.PromotedTo().PromoteValue() + m.CapturedPiece().Value() // undefended
54+
55+
// func sort_promotion(brd *Board, m Move) uint64 {
56+
// if is_attacked_by(brd, brd.AllOccupied()&sq_mask_off[m.From()],
57+
// m.To(), brd.Enemy(), brd.c) { // defended
58+
// if get_see(brd, m.From(), m.To(), m.CapturedPiece()) >= 0 {
59+
// return SORT_WINNING_PROMOTION | mvv_lva(m.CapturedPiece(), PAWN)
60+
// } else {
61+
// return mvv_lva(m.CapturedPiece(), PAWN)
62+
// }
63+
// } else {
64+
// // val = m.PromotedTo().PromoteValue() + m.CapturedPiece().Value() // undefended
65+
// return SORT_WINNING_PROMOTION | mvv_lva(m.CapturedPiece(), PAWN)
66+
// }
67+
// }
68+
69+
func sort_promotion_advances(brd *Board, from, to int, promoted_to Piece) uint64 {
70+
if is_attacked_by(brd, brd.AllOccupied()&sq_mask_off[from],
71+
to, brd.Enemy(), brd.c) { // defended
72+
see := get_see(brd, from, to, EMPTY)
73+
if see >= 0 {
74+
return SORT_WINNING_PROMOTION | uint64(see)
75+
} else {
76+
return uint64(SORT_LOSING_PROMOTION + see)
77+
}
78+
} else { // undefended
79+
return SORT_WINNING_PROMOTION | uint64(promoted_to.PromoteValue())
6280
}
63-
if val >= 0 {
64-
return SORT_WINNING | mvv_lva(m.CapturedPiece(), PAWN)
65-
} else {
66-
return mvv_lva(m.CapturedPiece(), PAWN)
81+
}
82+
83+
func sort_promotion_captures(brd *Board, from, to int, captured_piece, promoted_to Piece) uint64 {
84+
if is_attacked_by(brd, brd.AllOccupied()&sq_mask_off[from], to, brd.Enemy(), brd.c) { // defended
85+
return uint64(SORT_WINNING_PROMOTION + get_see(brd, from, to, captured_piece))
86+
} else { // undefended
87+
return SORT_WINNING_PROMOTION | uint64(promoted_to.PromoteValue()+captured_piece.Value())
6788
}
6889
}
6990

91+
func mvv_lva(victim, attacker Piece) uint64 { // returns value between 0 and 64
92+
return uint64(((victim+1)<<3)-attacker) << 22
93+
}
94+
7095
type SortItem struct {
7196
move Move
7297
order uint64
@@ -76,6 +101,12 @@ type MoveList []*SortItem
76101

77102
func (l *MoveList) Sort() {
78103
sort.Sort(l)
104+
// print_mutex.Lock()
105+
// fmt.Println("-------------")
106+
// for i, item := range *l {
107+
// fmt.Printf("%d %s %b\n", i+1, item.move.ToString(), item.order)
108+
// }
109+
// print_mutex.Unlock()
79110
}
80111

81112
func (l MoveList) Len() int { return len(l) }

0 commit comments

Comments
 (0)