github.com/gorgonia/agogo@v0.1.1/game/mnk/mnk.go (about) 1 package mnk 2 3 import ( 4 "fmt" 5 "hash/fnv" 6 "math/rand" 7 "sync" 8 9 "github.com/gorgonia/agogo/game" 10 ) 11 12 var ( 13 Pass = game.Single(-1) 14 15 Cross = game.Player(game.Black) 16 Nought = game.Player(game.White) 17 18 r = rand.New(rand.NewSource(1337)) 19 ) 20 21 var _ game.State = &MNK{} 22 23 // MNK is a representation of M,N,K games - a game is played on a MxN board. K moves to win. 24 type MNK struct { 25 sync.Mutex 26 board []game.Colour 27 m, n, k int 28 29 nextToMove game.Player 30 history []game.PlayerMove 31 historical [][]game.Colour 32 histPtr int 33 } 34 35 // New creates a new MNK game 36 func New(m, n, k int) *MNK { 37 return &MNK{ 38 board: make([]game.Colour, m*n), 39 history: make([]game.PlayerMove, 0, m*n), 40 historical: make([][]game.Colour, 0, m*n), 41 m: m, 42 n: n, 43 k: k, 44 } 45 } 46 47 // TicTacToe creates a new MNK game for Tic Tac Toe 48 func TicTacToe() *MNK { 49 return &MNK{ 50 board: make([]game.Colour, 9), 51 history: make([]game.PlayerMove, 0, 9), 52 historical: make([][]game.Colour, 0, 9), 53 m: 3, 54 n: 3, 55 k: 3, 56 } 57 } 58 59 func (g *MNK) Format(s fmt.State, c rune) { 60 for i, c := range g.board { 61 if i%g.n == 0 { 62 fmt.Fprint(s, "⎢ ") 63 } 64 fmt.Fprintf(s, "%s ", c) 65 if (i+1)%g.n == 0 && i != 0 { 66 fmt.Fprint(s, "⎥\n") 67 } 68 } 69 } 70 71 func (g *MNK) BoardSize() (int, int) { return g.m, g.n } 72 func (g *MNK) Board() []game.Colour { return g.board } 73 74 func (g *MNK) Historical(i int) []game.Colour { return g.historical[i] } 75 76 func (g *MNK) Hash() game.Zobrist { 77 h := fnv.New32a() 78 for _, v := range g.board { 79 fmt.Fprintf(h, "%v", v) 80 } 81 return game.Zobrist(h.Sum32()) 82 } 83 84 func (g *MNK) ActionSpace() int { return g.m * g.n } 85 86 func (g *MNK) SetToMove(p game.Player) { g.Lock(); g.nextToMove = p; g.Unlock() } 87 88 func (g *MNK) ToMove() game.Player { return g.nextToMove } 89 90 func (g *MNK) LastMove() game.PlayerMove { 91 if len(g.history) > 0 { 92 return g.history[g.histPtr-1] 93 } 94 return game.PlayerMove{game.Player(game.None), Pass} 95 } 96 97 // Passes always returns -1. You can't pass in tic-tac-toe 98 func (g *MNK) Passes() int { return -1 } 99 100 func (g *MNK) MoveNumber() int { return len(g.history) } 101 102 func (g *MNK) Check(m game.PlayerMove) bool { 103 if m.Single.IsResignation() { 104 return true 105 } 106 107 // Pass not allowed! 108 if m.Single.IsPass() { 109 return false 110 } 111 112 if int(m.Single) >= len(g.board) { 113 return false 114 } 115 116 if g.board[int(m.Single)] != game.None { 117 return false 118 } 119 return true 120 } 121 122 func (g *MNK) Apply(m game.PlayerMove) game.State { 123 if !g.Check(m) { 124 return g // no change to the state 125 } 126 127 hb := make([]game.Colour, len(g.board)) 128 g.Lock() 129 copy(hb, g.board) 130 g.board[int(m.Single)] = game.Colour(m.Player) 131 g.histPtr++ 132 if len(g.history) < g.histPtr { 133 g.history = append(g.history, m) 134 } else { 135 g.history[g.histPtr-1] = m 136 } 137 g.historical = append(g.historical, hb) 138 g.nextToMove = opponent(m.Player) 139 140 g.Unlock() 141 return g 142 } 143 144 // Handicap always returns 0. 145 func (g *MNK) Handicap() int { return 0 } 146 147 func (g *MNK) Score(p game.Player) float32 { 148 if g.isWinner(p) { 149 return 1 150 } 151 if g.isWinner(opponent(p)) { 152 return -2 153 } 154 return 0 // draw or incomplete 155 } 156 157 // AdditionalScore returns 0 always. No Komi 158 func (g *MNK) AdditionalScore() float32 { return 0 } 159 160 // Ended checks if the game has ended. If it has, who is the winner? 161 func (g *MNK) Ended() (ended bool, winner game.Player) { 162 if g.isWinner(Cross) { 163 return true, Cross 164 } 165 if g.isWinner(Nought) { 166 return true, Nought 167 } 168 for _, c := range g.board { 169 if c == game.None { 170 return false, game.Player(game.None) 171 } 172 } 173 return true, game.Player(game.None) 174 } 175 176 func (g *MNK) Reset() { 177 for i := range g.board { 178 g.board[i] = game.None 179 } 180 g.history = g.history[:0] 181 g.histPtr = 0 182 } 183 184 func (g *MNK) UndoLastMove() { 185 if len(g.history) > 0 { 186 g.board[int(g.history[g.histPtr-1].Single)] = game.None 187 g.histPtr-- 188 } 189 } 190 191 func (g *MNK) Fwd() { 192 if len(g.history) > 0 { 193 g.histPtr++ 194 } 195 } 196 197 func (g *MNK) Eq(other game.State) bool { 198 ot, ok := other.(*MNK) 199 if !ok { 200 return false 201 } 202 if len(g.board) != len(ot.board) { 203 return false 204 } 205 for i := range g.board { 206 if g.board[i] != ot.board[i] { 207 return false 208 } 209 } 210 return true 211 } 212 213 func (g *MNK) Clone() game.State { 214 retVal := New(g.m, g.n, g.k) 215 g.Lock() 216 copy(retVal.board, g.board) 217 retVal.history = make([]game.PlayerMove, len(g.history), g.m*g.n) 218 copy(retVal.history, g.history) 219 copy(retVal.historical, g.historical) 220 retVal.nextToMove = g.nextToMove 221 retVal.histPtr = g.histPtr 222 g.Unlock() 223 return retVal 224 } 225 226 func (g *MNK) isWinner(p game.Player) bool { 227 colour := game.Colour(p) 228 // check rows 229 for i := 0; i < g.m; i++ { 230 var rowCount int 231 for j := 0; j < g.n; j++ { 232 if g.board[i*g.n+j] == colour { 233 rowCount++ 234 } else { 235 rowCount-- 236 } 237 238 } 239 if rowCount >= g.k { 240 return true 241 } 242 } 243 // check cols 244 for j := 0; j < g.n; j++ { 245 var count int 246 for i := 0; i*g.n+j < len(g.board); i++ { 247 if g.board[i*g.n+j] == colour { 248 count++ 249 } else { 250 count = 0 251 } 252 } 253 if count >= g.k { 254 return true 255 } 256 } 257 258 for i := 0; i < g.m; i++ { 259 for j := 0; g.n-j > g.n-g.k && j < g.n; j++ { 260 idx := i*g.n + j 261 var diagCount int 262 for g.board[idx] == colour { 263 diagCount++ 264 if diagCount >= g.k { 265 return true 266 } 267 268 idx = idx + g.n + 1 269 if idx >= g.m*g.n { 270 break 271 } 272 } 273 } 274 } 275 276 for i := 0; i < g.m; i++ { 277 for j := g.n - 1; j >= g.k-1; j-- { 278 idx := i*g.n + j 279 var diagCount int 280 for g.board[idx] == colour { 281 diagCount++ 282 283 if diagCount >= g.k { 284 return true 285 } 286 287 idx = idx + g.n - 1 288 if idx >= g.m*g.n { 289 break 290 } 291 } 292 } 293 } 294 return false 295 } 296 297 func opponent(p game.Player) game.Player { 298 switch p { 299 case Cross: 300 return Nought 301 case Nought: 302 return Cross 303 } 304 panic("Unreachable") 305 }