github.com/gorgonia/agogo@v0.1.1/game/c4/c4.go (about) 1 package c4 2 3 import ( 4 "errors" 5 "fmt" 6 // "log" 7 8 "github.com/gorgonia/agogo/game" 9 "gorgonia.org/tensor" 10 "gorgonia.org/tensor/native" 11 ) 12 13 type Board struct { 14 data *tensor.Dense 15 it [][]game.Colour 16 n int // how many to be considered a win? 17 } 18 19 func newBoard(rows, cols, n int) *Board { 20 backing := make([]game.Colour, rows*cols) 21 data := tensor.New(tensor.WithShape(rows, cols), tensor.WithBacking(backing)) 22 iter, err := native.Matrix(data) 23 if err != nil { 24 panic(err) 25 } 26 it := iter.([][]game.Colour) 27 return &Board{ 28 data: data, 29 it: it, 30 n: n, 31 } 32 } 33 34 func (b *Board) Format(s fmt.State, c rune) { 35 switch c { 36 case 's', 'v': 37 for _, row := range b.it { 38 fmt.Fprint(s, "⎢ ") 39 for _, col := range row { 40 fmt.Fprintf(s, "%s ", col) 41 } 42 fmt.Fprint(s, "⎥\n") 43 } 44 } 45 } 46 47 func (b *Board) Apply(m game.PlayerMove) error { 48 if m.Single.IsPass() { 49 return nil 50 } 51 row, col, err := b.check(m) 52 if err != nil { 53 return err 54 } 55 b.it[row][col] = game.Colour(m.Player) 56 return nil 57 } 58 59 func (b *Board) check(m game.PlayerMove) (row, col int, err error) { 60 if m.Single.IsPass() { 61 return -1, -1, nil 62 } 63 col = int(m.Single) 64 for row = len(b.it) - 1; row >= 0; row-- { 65 if b.it[row][col] == game.None { 66 return row, col, nil 67 } 68 } 69 return -1, -1, errors.New("Selected column is full") 70 } 71 72 func (b *Board) clone() *Board { 73 sh := b.data.Shape() 74 75 b2 := newBoard(sh[0], sh[1], b.n) 76 raw2 := b2.data.Data().([]game.Colour) 77 raw := b.data.Data().([]game.Colour) 78 copy(raw2, raw) 79 return b2 80 } 81 82 func (b *Board) checkWin() game.Colour { 83 rows, cols := b.data.Shape()[0], b.data.Shape()[1] 84 if winner := b.checkVertical(rows, cols); winner != game.None { 85 return winner 86 } 87 if winner := b.checkHorizontal(rows, cols); winner != game.None { 88 return winner 89 } 90 if winner := b.checkTLBR(rows, cols); winner != game.None { 91 return winner 92 } 93 return b.checkTRBL(rows, cols) 94 } 95 96 // checkVertical checks downwards 97 func (b *Board) checkVertical(rows, cols int) game.Colour { 98 for x := 0; x < cols; x++ { 99 for y := 0; y < rows; y++ { 100 c := b.it[y][x] 101 winning := true 102 if c != game.None { 103 for i := 0; i < b.n; i++ { 104 if y+i < rows { 105 if b.it[y+i][x] != c { 106 winning = false 107 } 108 } else { 109 winning = false 110 } 111 } 112 if winning { 113 return c 114 } 115 } 116 } 117 } 118 return game.None 119 } 120 121 // checkHorizontal checks rightwards 122 func (b *Board) checkHorizontal(rows, cols int) game.Colour { 123 for x := 0; x < cols; x++ { 124 for y := 0; y < rows; y++ { 125 c := b.it[y][x] 126 winning := true 127 if c != game.None { 128 for i := 0; i < b.n; i++ { 129 if x+i < cols { 130 if b.it[y][x+i] != c { 131 winning = false 132 } 133 } else { 134 winning = false 135 } 136 } 137 if winning { 138 return c 139 } 140 } 141 } 142 } 143 return game.None 144 } 145 146 func (b *Board) checkTLBR(rows, cols int) game.Colour { 147 for x := 0; x < cols; x++ { 148 for y := 0; y < rows; y++ { 149 c := b.it[y][x] 150 winning := true 151 if c != game.None { 152 for i := 0; i < b.n; i++ { 153 if x-i >= 0 && y+i < rows { 154 if b.it[y+i][x-i] != c { 155 winning = false 156 } 157 } else { 158 winning = false 159 } 160 } 161 if winning { 162 return c 163 } 164 } 165 } 166 } 167 return game.None 168 } 169 170 func (b *Board) checkTRBL(rows, cols int) game.Colour { 171 for x := 0; x < cols; x++ { 172 for y := 0; y < rows; y++ { 173 c := b.it[y][x] 174 winning := true 175 if c != game.None { 176 for i := 0; i < b.n; i++ { 177 if x+i < cols && y+i < rows { 178 if b.it[y+i][x+i] != c { 179 winning = false 180 } 181 } else { 182 winning = false 183 } 184 } 185 if winning { 186 return c 187 } 188 } 189 } 190 } 191 return game.None 192 }