You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
3.8 KiB
179 lines
3.8 KiB
package main
|
|
// FIXME
|
|
// Working:
|
|
// - Moving
|
|
// - Approach captures
|
|
// - Withdrawl captures
|
|
// - Choosing randomly from moves with equal points
|
|
// Broken:
|
|
// - Continuations moves
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
)
|
|
|
|
var weakDirs = []dir{{0,-1}, {0,1}, {1,0}, {-1,0}}
|
|
var strongDirs = []dir{{-1,-1}, {-1,1}, {1,1}, {1,-1}}
|
|
|
|
type point struct {
|
|
row int
|
|
col int
|
|
}
|
|
|
|
|
|
type score struct {
|
|
d dir
|
|
attacking bool
|
|
continuation bool
|
|
deadCount int
|
|
approach bool
|
|
}
|
|
|
|
|
|
func (s score) points() int {
|
|
count := 0
|
|
if s.continuation {
|
|
count += 3
|
|
}
|
|
return count + s.deadCount
|
|
}
|
|
|
|
type place struct {
|
|
row int
|
|
col int
|
|
strong bool
|
|
s []score
|
|
maxPts int
|
|
bestScore []int
|
|
}
|
|
|
|
func (p *place) FindBestDir() {
|
|
if len(p.s) == 0 {
|
|
return
|
|
}
|
|
for i, s := range p.s {
|
|
if s.points() > p.maxPts {
|
|
p.maxPts = s.points()
|
|
p.bestScore = []int{i}
|
|
} else if s.points() == p.maxPts {
|
|
p.bestScore = append(p.bestScore, i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func ScoreContinuation(m move, g *Game, blacklist []point) (move, bool, error) {
|
|
p := place{m.toRow, m.toCol, g.board[m.toRow][m.toCol].strong, make([]score, 0, 8), 0, make([]int, 0, 7)}
|
|
p.Score(g)
|
|
outer: for _, s := range p.s {
|
|
if s.points() > 0 {
|
|
newPt := point{m.toRow+s.d.rowOff, m.toCol+s.d.colOff}
|
|
for _, bl := range blacklist {
|
|
if newPt.row == bl.row && newPt.col == bl.col {
|
|
continue outer
|
|
}
|
|
}
|
|
return move{m.toRow, m.toCol, newPt.row, newPt.col}, s.approach, nil
|
|
}
|
|
}
|
|
return move{}, false, fmt.Errorf("No valid continuation")
|
|
}
|
|
|
|
func (p *place) Score(g *Game) {
|
|
dirs := make([]dir, 0, 8)
|
|
dirs = append(dirs, weakDirs...)
|
|
if p.strong {
|
|
dirs = append(dirs, strongDirs...)
|
|
}
|
|
|
|
for _, d := range dirs {
|
|
m := move{p.row, p.col, p.row + d.rowOff, p.col + d.colOff}
|
|
err := g.ValidateMove(m)
|
|
if err != nil {
|
|
// This is not a valid move
|
|
continue
|
|
}
|
|
|
|
// Check continuation
|
|
hasContinuation := false
|
|
for _, d2 := range dirs {
|
|
if d2.rowOff == d.rowOff && d2.colOff == d.colOff {
|
|
// Cannot move in the same direction
|
|
continue
|
|
}
|
|
m2 := move{m.toRow, m.toCol, m.toRow + d2.rowOff, m.toCol + d2.colOff}
|
|
if g.withdrawAvailable(m2) || g.approachAvailable(m) {
|
|
hasContinuation = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// Check Attack
|
|
canWithdraw := g.withdrawAvailable(m)
|
|
canApproach := g.approachAvailable(m)
|
|
if !canWithdraw && !canApproach {
|
|
p.s = append(p.s, score{d, false, false, 0, false})
|
|
}
|
|
if canWithdraw {
|
|
p.s = append(p.s, score{d, true, hasContinuation, g.CountPotentialDead(m, false), false})
|
|
}
|
|
if canApproach {
|
|
p.s = append(p.s, score{d, true, hasContinuation, g.CountPotentialDead(m, true), true})
|
|
}
|
|
}
|
|
p.FindBestDir()
|
|
}
|
|
|
|
|
|
type cpu struct {
|
|
legal []place
|
|
bestScore int
|
|
bestMove []int
|
|
}
|
|
|
|
func (c *cpu) PickMove(g *Game) (move, bool, bool, bool) {
|
|
c.Clear()
|
|
c.GetLegalMoves(g)
|
|
|
|
for i := range c.legal {
|
|
if c.legal[i].maxPts > c.bestScore {
|
|
c.bestScore = c.legal[i].maxPts
|
|
c.bestMove = []int{i}
|
|
} else if c.legal[i].maxPts == c.bestScore {
|
|
c.bestMove = append(c.bestMove, i)
|
|
}
|
|
}
|
|
bestM := rand.Intn(len(c.bestMove))
|
|
m := c.legal[c.bestMove[bestM]]
|
|
bestS := rand.Intn(len(m.bestScore))
|
|
return move{
|
|
m.row,
|
|
m.col,
|
|
m.row+m.s[m.bestScore[bestS]].d.rowOff,
|
|
m.col+m.s[m.bestScore[bestS]].d.colOff},
|
|
m.s[m.bestScore[bestS]].approach,
|
|
m.s[m.bestScore[bestS]].attacking,
|
|
m.s[m.bestScore[bestS]].continuation
|
|
}
|
|
|
|
func (c *cpu) Clear() {
|
|
c.legal = make([]place, 0, 15)
|
|
c.bestMove = make([]int, 0, 5)
|
|
c.bestScore = 0
|
|
}
|
|
|
|
func (c *cpu) GetLegalMoves(g *Game) {
|
|
for r, row := range g.board {
|
|
for p, pt := range row {
|
|
if pt.state == Empty || pt.state == Black {
|
|
continue
|
|
}
|
|
pl := place{r, p, g.board[r][p].strong, make([]score, 0, 8), -1, make([]int, 0, 5)}
|
|
pl.Score(g)
|
|
if len(pl.s) > 0 {
|
|
c.legal = append(c.legal, pl)
|
|
}
|
|
}
|
|
}
|
|
}
|