board.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package board
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "log"
  8. "math/rand"
  9. "net/http"
  10. "time"
  11. )
  12. var setMask [4]byte = [4]byte{0xFC, 0xF3, 0xCF, 0x3F}
  13. var getMask [4]byte = [4]byte{0x03, 0x0C, 0x30, 0xC0}
  14. type Board struct {
  15. Width int `json:"cols"`
  16. Height int `json:"rows"`
  17. Finish Coordinate `json:"finish"`
  18. data []byte `json:"-"`
  19. }
  20. type req struct {
  21. x, y, width, height int
  22. }
  23. type response struct {
  24. boardTile io.ReadCloser
  25. x, y int
  26. }
  27. const maxIceWidth = 26
  28. // Create a new randomly generated board.
  29. func New(width, height int) *Board {
  30. return NewPartial(0, 0, width, height, width, height)
  31. }
  32. func NewPartial(startX, startY, width, height, totalWidth, totalHeight int) *Board {
  33. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  34. return newPartial(startX, startY, width, height, totalWidth, totalHeight, r)
  35. }
  36. func NewRemote(width, height int) *Board {
  37. b := &Board{
  38. Width: width,
  39. Height: height,
  40. data: make([]byte, (width*height)/4+1),
  41. }
  42. log.Printf("Start creating board %v x %v", width, height)
  43. regionWidth := 8
  44. regionHeight := 8
  45. regionWidthNum := width / regionWidth
  46. regionHeightNum := height / regionHeight
  47. returnChan := make(chan response)
  48. for i := 0; i < regionWidthNum; i++ {
  49. for j := 0; j < regionHeightNum; j++ {
  50. x := i * regionWidth
  51. y := j * regionHeight
  52. gbt := req{
  53. x: x,
  54. y: y,
  55. width: regionWidth,
  56. height: regionHeight,
  57. }
  58. go b.genRegion(gbt, returnChan)
  59. }
  60. }
  61. answers := 0
  62. for regionWidthNum+regionHeightNum > answers {
  63. result := <-returnChan
  64. n := -1
  65. for n != 0 {
  66. index := result.x*b.Width + result.y
  67. i := index / 4 // tiles per byte
  68. // TODO
  69. ba := b.data[i : i+regionWidth/4]
  70. n, _ = result.boardTile.Read(ba)
  71. }
  72. answers++
  73. }
  74. igloY := rand.Intn(height-2) + 1
  75. b.Set(width-2, igloY, Iglo)
  76. b.Finish.X = width - 2
  77. b.Finish.Y = igloY
  78. b.Set(width-2, igloY-1, Water)
  79. b.Set(width-2, igloY+1, Water)
  80. log.Printf("Finished creating board %v x %v\n", width, height)
  81. return b
  82. }
  83. func (b *Board) genRegion(req req, responseChan chan response) {
  84. requestUrl := fmt.Sprintf("http://localhost:8081/?totalWidth=%v&totalHeight=%vwidth=%v&height=%v&x=%v&y=%v", b.Width, b.Height, req.width, req.height, req.x, req.y)
  85. log.Printf("Requested generation of tile with url: %v\n", requestUrl)
  86. resp, _ := http.Get(requestUrl)
  87. result := response{
  88. x: req.x,
  89. y: req.y,
  90. boardTile: resp.Body,
  91. }
  92. responseChan <- result
  93. }
  94. func (b *Board) Set(x, y int, t TileType) {
  95. index := uint(y*b.Width + x)
  96. i := index / 4
  97. p := index % 4
  98. b.data[i] = (b.data[i] & setMask[p]) | byte(t)<<(p<<1)
  99. }
  100. func (b *Board) Get(x, y int) TileType {
  101. index := y*b.Width + x
  102. i := index / 4
  103. p := index % 4
  104. return TileType((b.data[i] & getMask[p]) >> uint(p<<1))
  105. }
  106. func (b *Board) WriteData(w io.Writer) {
  107. n, err := w.Write(b.data)
  108. if err != nil {
  109. fmt.Errorf("Error writing board after %v bytes: %v", n, err)
  110. }
  111. }
  112. func (b *Board) WriteJSON(w io.Writer, startCol, startRow, cols, rows int) {
  113. sc, sr, cols, rows := sanitizeViewPort(b, startCol, startRow, cols, rows)
  114. fmt.Fprintf(w, "{\"x\":%v,\"y\":%v,\"rows\":%v,\"cols\":%v,\"tiles\":[", sc, sr, cols, rows)
  115. for y := startRow; y < sr+rows; y++ {
  116. for x := startCol; x < sc+cols; x++ {
  117. if !(x == startCol && y == startRow) {
  118. w.Write([]byte{','})
  119. }
  120. t := &jsonTile{x, y, b.Get(x, y).Name(), ""}
  121. bs, _ := json.Marshal(t)
  122. w.Write(bs)
  123. }
  124. }
  125. fmt.Fprintf(w, "]}")
  126. }
  127. func (b *Board) String() string {
  128. var buffer bytes.Buffer
  129. for y := 0; y < b.Height; y++ {
  130. for x := 0; x < b.Width; x++ {
  131. buffer.WriteString(b.Get(x, y).String())
  132. }
  133. buffer.WriteString(fmt.Sprintln())
  134. }
  135. return buffer.String()
  136. }
  137. func new(width, height int, r *rand.Rand) *Board {
  138. return newPartial(0, 0, width, height, width, height, r)
  139. }
  140. func newPartial(startX, startY, width, height, totalWidth, totalHeight int, r *rand.Rand) *Board {
  141. b := &Board{
  142. Width: width,
  143. Height: height,
  144. data: make([]byte, (width*height)/4+1),
  145. }
  146. leftLimit := (totalWidth-maxIceWidth)/2 - startX
  147. rightLimit := totalWidth/2 + maxIceWidth/2 - startX
  148. mid := totalWidth/2 - startX
  149. for y := 0; y < height; y++ {
  150. for x := 0; x < width; x++ {
  151. switch {
  152. case r.Intn(10) > 7:
  153. b.Set(x, y, Rock)
  154. case x > leftLimit && x < rightLimit && r.Intn(maxIceWidth) >= abs(mid-x):
  155. b.Set(x, y, Ice)
  156. default:
  157. b.Set(x, y, Water)
  158. }
  159. }
  160. }
  161. return b
  162. }
  163. func sanitizeViewPort(b *Board, startCol, startRow, cols, rows int) (int, int, int, int) {
  164. if startCol > b.Width {
  165. startCol = b.Width
  166. }
  167. if startCol < 0 {
  168. cols += startCol
  169. startCol = 0
  170. }
  171. if startRow > b.Height {
  172. startRow = b.Height
  173. }
  174. if startRow < 0 {
  175. rows += startRow
  176. startRow = 0
  177. }
  178. if startCol+cols > b.Width {
  179. cols = b.Width - startCol
  180. }
  181. if startRow+rows > b.Height {
  182. rows = b.Height - startRow
  183. }
  184. if cols < 0 {
  185. cols = 0
  186. }
  187. if rows < 0 {
  188. rows = 0
  189. }
  190. return startCol, startRow, cols, rows
  191. }
  192. func abs(a int) int {
  193. if a < 0 {
  194. return -a
  195. }
  196. return a
  197. }