1 package tabula
2
3 import (
4 "fmt"
5 "log"
6 "math"
7 "sort"
8 "sync"
9 )
10
11 var (
12 AnalysisBufferSize = 128
13 SubAnalysisBufferSize = 3072
14 )
15
16 const (
17 SpaceHomePlayer int8 = 0
18 SpaceHomeOpponent int8 = 25
19 SpaceBarPlayer int8 = 26
20 SpaceBarOpponent int8 = 27
21 SpaceRoll1 int8 = 28
22 SpaceRoll2 int8 = 29
23 SpaceRoll3 int8 = 30
24 SpaceRoll4 int8 = 31
25 SpaceEnteredPlayer int8 = 32
26 SpaceEnteredOpponent int8 = 33
27 SpaceVariant int8 = 34
28 )
29
30 const (
31 boardSpaces = 35
32 )
33
34 const (
35 VariantBackgammon int8 = 0
36 VariantAceyDeucey int8 = 1
37 VariantTabula int8 = 2
38 )
39
40
41
42 type Board [boardSpaces]int8
43
44
45 func NewBoard(variant int8) Board {
46 if variant != VariantBackgammon {
47 return Board{15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 0, 0, 0, 1}
48 }
49 return Board{0, -2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, -5, 5, 0, 0, 0, -3, 0, -5, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}
50 }
51
52 func (b Board) SetValue(space int, value int8) Board {
53 b[space] = value
54 return b
55 }
56
57
58 func (b Board) Move(from int8, to int8, player int8) Board {
59 if b[from] == 0 || (player == 1 && b[from] < 0) || (player == 2 && b[from] > 0) {
60 log.Panic("illegal move: no from checker", from, to, player)
61 } else if b[to] != 0 {
62 if (player == 1 && b[to] == -1) || (player == 2 && b[to] == 1) {
63 b[to] = 0
64 if player == 1 {
65 b[SpaceBarOpponent]--
66 } else {
67 b[SpaceBarPlayer]++
68 }
69 } else if (player == 1 && b[to] < 0) || (player == 2 && b[to] > 0) {
70 b.Print()
71 log.Panic("illegal move: existing checkers at to space", from, to, player, b[to])
72 }
73 }
74 delta := int8(1)
75 if player == 2 {
76 delta = -1
77 }
78 b[from], b[to] = b[from]-delta, b[to]+delta
79 if (player == 1 && from == SpaceHomePlayer && b[SpaceEnteredPlayer] == 0 && b[SpaceHomePlayer] == 0) || (player == 2 && from == SpaceHomeOpponent && b[SpaceEnteredOpponent] == 0 && b[SpaceHomeOpponent] == 0) {
80 if player == 1 {
81 b[SpaceEnteredPlayer] = 1
82 } else {
83 b[SpaceEnteredOpponent] = 1
84 }
85 }
86 return b
87 }
88
89
90 func checkers(player int8, v int8) int8 {
91 if player == 1 && v > 0 {
92 return v
93 } else if player == 2 && v < 0 {
94 return v * -1
95 }
96 return 0
97 }
98
99 func (b Board) MayBearOff(player int8) bool {
100 if b[SpaceVariant] != VariantBackgammon && ((player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0)) {
101 return false
102 } else if b[SpaceVariant] == VariantTabula && !b.SecondHalf(player) {
103 return false
104 }
105 barSpace := SpaceBarPlayer
106 if player == 2 {
107 barSpace = SpaceBarOpponent
108 }
109 if checkers(player, b[barSpace]) != 0 {
110 return false
111 }
112 if b[SpaceVariant] != VariantTabula {
113 if player == 1 {
114 for space := 24; space > 6; space-- {
115 if checkers(player, b[space]) != 0 {
116 return false
117 }
118 }
119 } else {
120 for space := 1; space < 19; space++ {
121 if checkers(player, b[space]) != 0 {
122 return false
123 }
124 }
125 }
126 }
127 return true
128 }
129
130 func (b Board) spaceDiff(player int8, from int8, to int8) int8 {
131 switch {
132 case from < 0 || from > 27 || to < 0 || to > 27:
133 return 0
134 case to == SpaceBarPlayer || to == SpaceBarOpponent:
135 return 0
136 case (from == SpaceHomePlayer || from == SpaceHomeOpponent || from == SpaceBarPlayer || from == SpaceBarOpponent) && (to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomePlayer || to == SpaceHomeOpponent):
137 return 0
138 case to == SpaceHomePlayer:
139 if player == 2 {
140 return 0
141 }
142 if b[SpaceVariant] == VariantTabula {
143 if (player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0) || !b.SecondHalf(player) {
144 return 0
145 }
146 return 25 - from
147 }
148 return from
149 case to == SpaceHomeOpponent:
150 if player == 1 {
151 return 0
152 }
153 return 25 - from
154 case from == SpaceHomePlayer || from == SpaceHomeOpponent:
155 if (player == 1 && from == SpaceHomeOpponent) || (player == 2 && from == SpaceHomePlayer) {
156 return 0
157 }
158 switch b[SpaceVariant] {
159 case VariantAceyDeucey:
160 if player == 1 && from == SpaceHomePlayer && b[SpaceEnteredPlayer] == 0 {
161 return 25 - to
162 } else if player == 2 && from == SpaceHomeOpponent && b[SpaceEnteredOpponent] == 0 {
163 return to
164 }
165 case VariantTabula:
166 if (player == 1 && from != SpaceHomePlayer && b[SpaceEnteredPlayer] == 0) || (player == 2 && from != SpaceHomeOpponent && b[SpaceEnteredOpponent] == 0) {
167 return 0
168 }
169 return to
170 }
171 return 0
172 case from == SpaceBarPlayer:
173 if player == 2 {
174 return 0
175 }
176 if b[SpaceVariant] == VariantTabula {
177 return to
178 }
179 return 25 - to
180 case from == SpaceBarOpponent:
181 if player == 1 {
182 return 0
183 }
184 return to
185 default:
186 diff := to - from
187 if diff < 0 {
188 return diff * -1
189 }
190 return diff
191 }
192 }
193
194
195 func (b Board) HaveRoll(from int8, to int8, player int8) bool {
196 barSpace := SpaceBarPlayer
197 if player == 2 {
198 barSpace = SpaceBarOpponent
199 }
200 if b[barSpace] != 0 && from != barSpace {
201 return false
202 }
203
204 if b[SpaceVariant] == VariantTabula && to > 12 && to < 25 && ((player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0)) {
205 return false
206 }
207
208 delta := b.spaceDiff(player, from, to)
209 if delta == 0 {
210 return false
211 }
212
213 if b[SpaceRoll1] == delta || b[SpaceRoll2] == delta || b[SpaceRoll3] == delta || b[SpaceRoll4] == delta {
214 return true
215 }
216
217 playerDelta := -1
218 playerHomeEnd := 6
219 if player == 2 {
220 playerDelta = 1
221 playerHomeEnd = 19
222 }
223 if b.MayBearOff(player) && b[SpaceVariant] == VariantBackgammon {
224 allowGreater := true
225 for checkSpace := 0; checkSpace < 6-int(delta); checkSpace++ {
226 if checkers(player, b[playerHomeEnd+checkSpace*playerDelta]) != 0 {
227 allowGreater = false
228 break
229 }
230 }
231 if allowGreater {
232 return (b[SpaceRoll1] >= delta || b[SpaceRoll2] >= delta || b[SpaceRoll3] >= delta || b[SpaceRoll4] >= delta)
233 }
234 }
235 return false
236 }
237
238
239 func (b Board) UseRoll(from int8, to int8, player int8) Board {
240 delta := b.spaceDiff(player, from, to)
241 if delta == 0 {
242 b.Print()
243 log.Panic("unknown space diff", from, to, player)
244 }
245
246 switch {
247 case b[SpaceRoll1] == delta:
248 b[SpaceRoll1] = 0
249 return b
250 case b[SpaceRoll2] == delta:
251 b[SpaceRoll2] = 0
252 return b
253 case b[SpaceRoll3] == delta:
254 b[SpaceRoll3] = 0
255 return b
256 case b[SpaceRoll4] == delta:
257 b[SpaceRoll4] = 0
258 return b
259 }
260
261 playerDelta := -1
262 playerHomeEnd := 6
263 if player == 2 {
264 playerDelta = 1
265 playerHomeEnd = 19
266 }
267 var allowGreater bool
268 if b.MayBearOff(player) && b[SpaceVariant] == VariantBackgammon {
269 allowGreater = true
270 for checkSpace := int8(0); checkSpace < 6-delta; checkSpace++ {
271 if checkers(player, b[playerHomeEnd+int(checkSpace)*playerDelta]) != 0 {
272 allowGreater = false
273 break
274 }
275 }
276 }
277 if !allowGreater {
278 b.Print()
279 log.Panic(fmt.Sprint(b), "no available roll for move", from, to, player, delta)
280 }
281
282 switch {
283 case b[SpaceRoll1] >= delta:
284 b[SpaceRoll1] = 0
285 case b[SpaceRoll2] >= delta:
286 b[SpaceRoll2] = 0
287 case b[SpaceRoll3] >= delta:
288 b[SpaceRoll3] = 0
289 case b[SpaceRoll4] >= delta:
290 b[SpaceRoll4] = 0
291 default:
292 b.Print()
293 log.Panic(fmt.Sprint(b), "no available roll for move", from, to, player, delta)
294 }
295 return b
296 }
297
298 func (b Board) _available(player int8) [][2]int8 {
299 homeSpace := SpaceHomePlayer
300 barSpace := SpaceBarPlayer
301 opponentBarSpace := SpaceBarOpponent
302 if player == 2 {
303 homeSpace = SpaceHomeOpponent
304 barSpace = SpaceBarOpponent
305 opponentBarSpace = SpaceBarPlayer
306 }
307 mayBearOff := b.MayBearOff(player)
308 onBar := b[barSpace] != 0
309
310 var moves [][2]int8
311
312 if b[SpaceVariant] != VariantBackgammon && ((player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0)) && b[homeSpace] != 0 {
313 for space := int8(1); space < 25; space++ {
314 v := b[space]
315 if ((player == 1 && v >= -1) || (player == 2 && v <= 1)) && b.HaveRoll(homeSpace, space, player) {
316 moves = append(moves, [2]int8{homeSpace, space})
317 }
318 }
319 }
320 for from := int8(0); from < 28; from++ {
321 if from == SpaceHomePlayer || from == SpaceHomeOpponent || from == opponentBarSpace || checkers(player, b[from]) == 0 || (onBar && from != barSpace) {
322 continue
323 }
324 if player == 1 && b[SpaceVariant] != VariantTabula {
325 for to := int8(0); to < from; to++ {
326 if to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomeOpponent || (to == SpaceHomePlayer && !mayBearOff) {
327 continue
328 }
329 v := b[to]
330 if (player == 1 && v < -1) || (player == 2 && v > 1) || !b.HaveRoll(from, to, player) {
331 continue
332 }
333 moves = append(moves, [2]int8{from, to})
334 }
335 } else {
336 start := from + 1
337 if from == SpaceBarPlayer || from == SpaceBarOpponent {
338 start = 1
339 }
340 for i := start; i <= 25; i++ {
341 to := i
342 if player == 1 && to == SpaceHomeOpponent {
343 to = SpaceHomePlayer
344 } else if player == 2 && to == SpaceHomePlayer {
345 to = SpaceHomeOpponent
346 }
347 if to == SpaceBarPlayer || to == SpaceBarOpponent || (((player == 1 && to == SpaceHomePlayer) || (player == 2 && to == SpaceHomeOpponent)) && !mayBearOff) {
348 continue
349 }
350 v := b[to]
351 if (player == 1 && v < -1) || (player == 2 && v > 1) || !b.HaveRoll(from, to, player) {
352 continue
353 }
354 moves = append(moves, [2]int8{from, to})
355 }
356 }
357 }
358
359 return moves
360 }
361
362
363 func (b Board) Available(player int8) ([][4][2]int8, []Board) {
364 var allMoves [][4][2]int8
365
366 resultMutex := &sync.Mutex{}
367 movesFound := func(moves [4][2]int8) bool {
368 resultMutex.Lock()
369 for i := range allMoves {
370 if movesEqual(allMoves[i], moves) {
371 resultMutex.Unlock()
372 return true
373 }
374 }
375 resultMutex.Unlock()
376 return false
377 }
378
379 var boards []Board
380 a := b._available(player)
381 maxLen := 1
382 for _, move := range a {
383 newBoard := b.UseRoll(move[0], move[1], player).Move(move[0], move[1], player)
384 newAvailable := newBoard._available(player)
385 if len(newAvailable) == 0 {
386 moves := [4][2]int8{move}
387 if !movesFound(moves) {
388 allMoves = append(allMoves, moves)
389 boards = append(boards, newBoard)
390 }
391 continue
392 }
393 for _, move2 := range newAvailable {
394 newBoard2 := newBoard.UseRoll(move2[0], move2[1], player).Move(move2[0], move2[1], player)
395 newAvailable2 := newBoard2._available(player)
396 if len(newAvailable2) == 0 {
397 moves := [4][2]int8{move, move2}
398 if !movesFound(moves) {
399 allMoves = append(allMoves, moves)
400 boards = append(boards, newBoard2)
401 if maxLen <= 2 {
402 maxLen = 2
403 }
404 }
405 continue
406 }
407 for _, move3 := range newAvailable2 {
408 newBoard3 := newBoard2.UseRoll(move3[0], move3[1], player).Move(move3[0], move3[1], player)
409 newAvailable3 := newBoard3._available(player)
410 if len(newAvailable3) == 0 {
411 moves := [4][2]int8{move, move2, move3}
412 if !movesFound(moves) {
413 allMoves = append(allMoves, moves)
414 boards = append(boards, newBoard3)
415 if maxLen <= 2 {
416 maxLen = 3
417 }
418 }
419 continue
420 }
421 for _, move4 := range newAvailable3 {
422 newBoard4 := newBoard3.UseRoll(move4[0], move4[1], player).Move(move4[0], move4[1], player)
423 moves := [4][2]int8{move, move2, move3, move4}
424 if !movesFound(moves) {
425 allMoves = append(allMoves, moves)
426 boards = append(boards, newBoard4)
427 maxLen = 4
428 }
429 }
430 }
431 }
432 }
433 var newMoves [][4][2]int8
434 for i := 0; i < len(allMoves); i++ {
435 l := 0
436 if (allMoves[i][3][0] != 0 || allMoves[i][3][1] != 0) && allMoves[i][2][0] == 0 && allMoves[i][2][1] == 0 {
437 allMoves[i][2][0], allMoves[i][2][1] = allMoves[i][3][0], allMoves[i][3][1]
438 allMoves[i][2][0], allMoves[i][2][1] = 0, 0
439 }
440 if (allMoves[i][2][0] != 0 || allMoves[i][2][1] != 0) && allMoves[i][1][0] == 0 && allMoves[i][1][1] == 0 {
441 allMoves[i][1][0], allMoves[i][1][1] = allMoves[i][2][0], allMoves[i][2][1]
442 allMoves[i][2][0], allMoves[i][2][1] = 0, 0
443 }
444 if (allMoves[i][1][0] != 0 || allMoves[i][1][1] != 0) && allMoves[i][0][0] == 0 && allMoves[i][0][1] == 0 {
445 allMoves[i][0][0], allMoves[i][0][1] = allMoves[i][1][0], allMoves[i][1][1]
446 allMoves[i][1][0], allMoves[i][1][1] = 0, 0
447 }
448 for j := 0; j < 4; j++ {
449 if allMoves[i][j][0] == 0 && allMoves[i][j][1] == 0 {
450 break
451 }
452 l = j + 1
453 }
454 if l >= maxLen {
455 newMoves = append(newMoves, allMoves[i])
456 }
457 }
458 return newMoves, boards
459 }
460
461 func (b Board) FirstLast(player int8) (playerFirst int8, opponentLast int8) {
462 playerFirst, opponentLast = -1, -1
463 if b[SpaceBarPlayer] != 0 || b[SpaceBarOpponent] != 0 || b[SpaceVariant] == VariantTabula {
464 return playerFirst, opponentLast
465 } else if b[SpaceVariant] == VariantAceyDeucey && ((b[SpaceEnteredPlayer] == 0 && b[SpaceHomePlayer] != 0) || (b[SpaceEnteredOpponent] == 0 && b[SpaceHomeOpponent] != 0)) {
466 return playerFirst, opponentLast
467 }
468 for space := int8(1); space < 25; space++ {
469 v := b[space]
470 if v == 0 {
471 continue
472 } else if v > 0 {
473 if space > playerFirst {
474 playerFirst = space
475 }
476 } else {
477 if opponentLast == -1 {
478 opponentLast = space
479 }
480 }
481 }
482 if player == 2 {
483 return opponentLast, playerFirst
484 }
485 return playerFirst, opponentLast
486 }
487
488 func (b Board) Past() bool {
489 playerFirst, opponentLast := b.FirstLast(1)
490 if playerFirst == -1 || opponentLast == -1 {
491 return false
492 }
493 return playerFirst < opponentLast
494 }
495
496 func (b Board) SecondHalf(player int8) bool {
497 if b[SpaceVariant] != VariantTabula {
498 return false
499 }
500
501 switch player {
502 case 1:
503 if b[SpaceBarPlayer] != 0 {
504 return false
505 } else if b[SpaceEnteredPlayer] == 0 && b[SpaceHomePlayer] != 0 {
506 return false
507 }
508 case 2:
509 if b[SpaceBarOpponent] != 0 {
510 return false
511 } else if b[SpaceEnteredOpponent] == 0 && b[SpaceHomeOpponent] != 0 {
512 return false
513 }
514 default:
515 log.Panicf("unknown player: %d", player)
516 }
517
518 for space := 1; space < 13; space++ {
519 v := b[space]
520 if (player == 1 && v > 0) || (player == 2 && v < 0) {
521 return false
522 }
523 }
524
525 return true
526 }
527
528 func (b Board) Pips(player int8) int {
529 var pips int
530 if b[SpaceVariant] != VariantBackgammon {
531 if player == 1 && b[SpaceEnteredPlayer] == 0 {
532 pips += int(checkers(player, b[SpaceHomePlayer])) * PseudoPips(player, SpaceHomePlayer, b[SpaceVariant])
533 } else if player == 2 && b[SpaceEnteredOpponent] == 0 {
534 pips += int(checkers(player, b[SpaceHomeOpponent])) * PseudoPips(player, SpaceHomeOpponent, b[SpaceVariant])
535 }
536 }
537 if player == 1 {
538 pips += int(checkers(player, b[SpaceBarPlayer])) * PseudoPips(player, SpaceBarPlayer, b[SpaceVariant])
539 } else {
540 pips += int(checkers(player, b[SpaceBarOpponent])) * PseudoPips(player, SpaceBarOpponent, b[SpaceVariant])
541 }
542 for space := int8(1); space < 25; space++ {
543 pips += int(checkers(player, b[space])) * PseudoPips(player, space, b[SpaceVariant])
544 }
545 return pips
546 }
547
548 func (b Board) Blots(player int8) int {
549 _, last := b.FirstLast(player)
550 o := opponent(player)
551 var pips int
552 var pastBlots int
553 var div int
554 for space := int8(1); space < 25; space++ {
555 if checkers(player, b[space]) == 1 {
556 if last != -1 && ((player == 1 && space < last) || (player == 2 && space > last)) && pastBlots == 0 {
557 pastBlots++
558 div = 4
559 } else {
560 div = 1
561 }
562 v := PseudoPips(o, space, b[SpaceVariant]) / div
563 if v < 1 {
564 v = 1
565 }
566 pips += v
567 }
568 }
569 return pips
570 }
571
572 func (b Board) evaluate(player int8, hitScore int, a *Analysis) {
573 pips := b.Pips(player)
574 score := float64(pips)
575 var blots int
576 if !a.Past {
577 blots = b.Blots(player)
578 score += float64(blots)*WeightBlot + float64(hitScore)*WeightHit
579 }
580 a.Pips = pips
581 a.Blots = blots
582 a.Hits = hitScore
583 a.PlayerScore = score
584 a.hitScore = hitScore
585 }
586
587 func (b Board) Evaluation(player int8, hitScore int, moves [4][2]int8) *Analysis {
588 a := &Analysis{
589 Board: b,
590 Moves: moves,
591 Past: b.Past(),
592 player: player,
593 chance: 1,
594 }
595 b.evaluate(player, hitScore, a)
596 return a
597 }
598
599 func (b Board) Analyze(available [][4][2]int8, result *[]*Analysis) {
600 if len(available) == 0 {
601 *result = (*result)[:0]
602 return
603 }
604 const priorityScore = -1000000
605
606 var reuse []*[]*Analysis
607 for _, r := range *result {
608 if r.result != nil {
609 reuse = append(reuse, r.result)
610 }
611 }
612 *result = (*result)[:0]
613 reuseLen := len(reuse)
614 var reuseIndex int
615
616 w := &sync.WaitGroup{}
617
618 past := b.Past()
619 w.Add(len(available))
620 for _, moves := range available {
621 var r *[]*Analysis
622 if reuseIndex < reuseLen {
623 r = reuse[reuseIndex]
624 *r = (*r)[:0]
625 reuseIndex++
626 } else {
627 v := make([]*Analysis, 0, SubAnalysisBufferSize)
628 r = &v
629 }
630 a := &Analysis{
631 Board: b,
632 Moves: moves,
633 Past: past,
634 player: 1,
635 chance: 1,
636 result: r,
637 resultMutex: &sync.Mutex{},
638 wg: w,
639 }
640 *result = append(*result, a)
641 analysisQueue <- a
642 }
643 w.Wait()
644
645 for _, a := range *result {
646 if a.player == 1 && !a.Past {
647 var oppPips float64
648 var oppBlots float64
649 var oppHits float64
650 var oppScore float64
651 var count float64
652 for _, r := range *a.result {
653 oppPips += float64(r.Pips)
654 oppBlots += float64(r.Blots)
655 oppHits += float64(r.Hits)
656 oppScore += r.PlayerScore
657 count++
658 }
659 if count == 0 {
660 a.Score = a.PlayerScore
661 } else {
662 a.OppPips = (oppPips / count)
663 a.OppBlots = (oppBlots / count)
664 a.OppHits = (oppHits / count)
665 a.OppScore = (oppScore / count)
666 score := a.PlayerScore
667 if !math.IsNaN(oppScore) {
668 score += a.OppScore * WeightOppScore
669 }
670 a.Score = score
671 }
672 } else {
673 a.Score = a.PlayerScore
674 }
675 if a.player == 1 && !past && a.Past {
676 a.Score += priorityScore
677 }
678 }
679
680 if b[SpaceVariant] != VariantTabula && b.StartingPosition(1) {
681 r1, r2 := b[SpaceRoll1], b[SpaceRoll2]
682 if r2 > r1 {
683 r1, r2 = r2, r1
684 }
685 var opening [4][2]int8
686 if r1 == r2 {
687 switch r1 {
688 case 1:
689 opening = [4][2]int8{{24, 23}, {24, 23}, {6, 5}, {6, 5}}
690 case 2:
691 opening = [4][2]int8{{13, 11}, {13, 11}, {11, 9}, {11, 9}}
692 case 3:
693 opening = [4][2]int8{{13, 10}, {13, 10}, {10, 7}, {10, 7}}
694 case 4:
695 opening = [4][2]int8{{13, 9}, {13, 9}, {6, 2}, {6, 2}}
696 case 5:
697 opening = [4][2]int8{{13, 8}, {13, 8}, {8, 3}, {8, 3}}
698 case 6:
699 opening = [4][2]int8{{24, 18}, {24, 18}, {13, 7}, {13, 7}}
700 }
701 } else {
702 switch r1 {
703 case 2:
704 opening = [4][2]int8{{13, 11}, {6, 5}}
705 case 3:
706 switch r2 {
707 case 1:
708 opening = [4][2]int8{{8, 5}, {6, 5}}
709 case 2:
710 opening = [4][2]int8{{13, 11}, {13, 10}}
711 }
712 case 4:
713 switch r2 {
714 case 1:
715 opening = [4][2]int8{{24, 23}, {13, 9}}
716 case 2:
717 opening = [4][2]int8{{8, 4}, {6, 4}}
718 case 3:
719 opening = [4][2]int8{{13, 10}, {13, 9}}
720 }
721 case 5:
722 switch r2 {
723 case 1:
724 opening = [4][2]int8{{24, 23}, {13, 8}}
725 case 2:
726 opening = [4][2]int8{{24, 22}, {13, 8}}
727 case 3:
728 opening = [4][2]int8{{8, 3}, {6, 3}}
729 case 4:
730 opening = [4][2]int8{{24, 20}, {13, 8}}
731 }
732 case 6:
733 switch r2 {
734 case 1:
735 opening = [4][2]int8{{13, 7}, {8, 7}}
736 case 2:
737 opening = [4][2]int8{{24, 18}, {13, 11}}
738 case 3:
739 opening = [4][2]int8{{24, 18}, {13, 10}}
740 case 4:
741 opening = [4][2]int8{{8, 2}, {6, 2}}
742 case 5:
743 opening = [4][2]int8{{24, 18}, {18, 13}}
744 }
745 }
746 }
747 for _, a := range *result {
748 if movesEqual(a.Moves, opening) {
749 a.Score = priorityScore
750 break
751 }
752 }
753 }
754
755 sort.Slice(*result, func(i, j int) bool {
756 return (*result)[i].Score < (*result)[j].Score
757 })
758 }
759
760 func (b Board) StartingPosition(player int8) bool {
761 if player == 1 {
762 return b[6] == 5 && b[8] == 3 && b[13] == 5 && b[24] == 2
763 }
764 return b[1] == -2 && b[12] == -5 && b[17] == -3 && b[19] == -5
765 }
766
767 func (b Board) ChooseDoubles(result *[]*Analysis) int {
768 if b[SpaceVariant] != VariantAceyDeucey {
769 return 0
770 }
771
772 bestDoubles := 6
773 bestScore := math.MaxFloat64
774
775 var available [][4][2]int8
776 for i := 0; i < 6; i++ {
777 doubles := int8(i + 1)
778 bc := b
779 bc[SpaceRoll1], bc[SpaceRoll2], bc[SpaceRoll3], bc[SpaceRoll4] = doubles, doubles, doubles, doubles
780
781 available, _ = bc.Available(1)
782 bc.Analyze(available, result)
783 if len(*result) > 0 && (*result)[0].Score < bestScore {
784 bestDoubles = i + 1
785 bestScore = (*result)[0].Score
786 }
787 }
788
789 return bestDoubles
790 }
791
792 func (b Board) Print() {
793 log.Printf("%+v", b)
794 }
795
796 func opponent(player int8) int8 {
797 if player == 1 {
798 return 2
799 }
800 return 1
801 }
802
803 func spaceValue(player int8, space int8, variant int8) int {
804 if space == SpaceHomePlayer || space == SpaceHomeOpponent || space == SpaceBarPlayer || space == SpaceBarOpponent {
805 return 25
806 } else if player == 1 || variant == VariantTabula {
807 return int(space)
808 } else {
809 return int(25 - space)
810 }
811 }
812
813 func PseudoPips(player int8, space int8, variant int8) int {
814 v := 6 + spaceValue(player, space, variant) + int(math.Exp(float64(spaceValue(player, space, variant))*0.2))*2
815 if space == SpaceHomePlayer || space == SpaceHomeOpponent || (variant == VariantTabula && space < 13) || (variant != VariantTabula && ((player == 1 && (space > 6 || space == SpaceBarPlayer)) || (player == 2 && (space < 19 || space == SpaceBarOpponent)))) {
816 v += 24
817 }
818 return v
819 }
820
821 func movesEqual(a [4][2]int8, b [4][2]int8) bool {
822 if a[0][0] == b[0][0] && a[0][1] == b[0][1] {
823 if a[1][0] == b[1][0] && a[1][1] == b[1][1] {
824 if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) ||
825 (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) {
826 return true
827 }
828 }
829 if a[1][0] == b[2][0] && a[1][1] == b[2][1] {
830 if (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) ||
831 (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) {
832 return true
833 }
834 }
835 if a[1][0] == b[3][0] && a[1][1] == b[3][1] {
836 if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) ||
837 (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) {
838 return true
839 }
840 }
841 }
842 if a[0][0] == b[1][0] && a[0][1] == b[1][1] {
843 if a[1][0] == b[0][0] && a[1][1] == b[0][1] {
844 if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) ||
845 (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) {
846 return true
847 }
848 }
849 if a[1][0] == b[2][0] && a[1][1] == b[2][1] {
850 if (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) ||
851 (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) {
852 return true
853 }
854 }
855 if a[1][0] == b[3][0] && a[1][1] == b[3][1] {
856 if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) ||
857 (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) {
858 return true
859 }
860 }
861 }
862 if a[0][0] == b[2][0] && a[0][1] == b[2][1] {
863 if a[1][0] == b[0][0] && a[1][1] == b[0][1] {
864 if (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) ||
865 (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) {
866 return true
867 }
868 }
869 if a[1][0] == b[1][0] && a[1][1] == b[1][1] {
870 if (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) ||
871 (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) {
872 return true
873 }
874 }
875 if a[1][0] == b[3][0] && a[1][1] == b[3][1] {
876 if (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) ||
877 (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) {
878 return true
879 }
880 }
881 }
882 if a[0][0] == b[3][0] && a[0][1] == b[3][1] {
883 if a[1][0] == b[0][0] && a[1][1] == b[0][1] {
884 if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) ||
885 (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) {
886 return true
887 }
888 }
889 if a[1][0] == b[1][0] && a[1][1] == b[1][1] {
890 if (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) ||
891 (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) {
892 return true
893 }
894 }
895 if a[1][0] == b[2][0] && a[1][1] == b[2][1] {
896 if (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) ||
897 (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) {
898 return true
899 }
900 }
901 }
902 return false
903 }
904
View as plain text