main.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "math"
  9. "math/rand"
  10. "net/http"
  11. "os"
  12. "strconv"
  13. "time"
  14. "battlecamp-go/board"
  15. "battlecamp-go/events"
  16. "battlecamp-go/flag"
  17. "battlecamp-go/game"
  18. "battlecamp-go/player"
  19. "battlecamp-go/stomp"
  20. )
  21. func main() {
  22. initLogging()
  23. log.Println("Game bot version 0.1")
  24. flag.ParseFlags()
  25. pu := make(chan *events.PlayerUpdate)
  26. go subscribeToUpdate(pu)
  27. for {
  28. subscribeToGame(pu)
  29. }
  30. }
  31. func subscribeToGame(pu chan *events.PlayerUpdate) {
  32. sub := stomp.Subscribe("game")
  33. gameEndChannels := make(map[int]*[]chan bool)
  34. for {
  35. announcement := <-sub
  36. gs := new(events.GameStart)
  37. json.Unmarshal(announcement.Body, &gs)
  38. fmt.Printf("announcement type: %v for game %v\n", gs.Type, strconv.FormatInt(gs.GameId, 10))
  39. if "GAME_START" == gs.Type {
  40. names := []string{"Zeus"}
  41. tmp := make([]chan bool, 0, len(names))
  42. gameEndChannels[int(gs.GameId)] = &tmp
  43. for _, name := range names {
  44. gameEndChan := make(chan bool)
  45. tmp := append(*gameEndChannels[int(gs.GameId)], gameEndChan)
  46. gameEndChannels[int(gs.GameId)] = &tmp
  47. go joinGame(gs.GameId, gameEndChan, pu, name)
  48. }
  49. } else {
  50. chans := gameEndChannels[int(gs.GameId)]
  51. for _, channel := range *chans {
  52. channel <- true
  53. }
  54. }
  55. }
  56. }
  57. func subscribeToUpdate(pu chan *events.PlayerUpdate) {
  58. sub := stomp.Subscribe("update")
  59. for {
  60. announcement := <-sub
  61. if "vnd.battlecamp.player" == announcement.ContentType {
  62. p := new(player.Player)
  63. json.Unmarshal(announcement.Body, &p)
  64. playerJoin(p)
  65. } else {
  66. pue := new(events.PlayerUpdate)
  67. json.Unmarshal(announcement.Body, &pue)
  68. pu <- pue
  69. }
  70. }
  71. }
  72. func joinGame(gameId int64, gameEndChan chan bool, pu chan *events.PlayerUpdate, name string) {
  73. //join game
  74. p := &player.Player{
  75. Id: name,
  76. Color: "#238b02",
  77. Type: 1,
  78. }
  79. players := make([]*player.Player, 5)
  80. //retrieve finish
  81. gameUrl := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10)
  82. fmt.Println("getGame:>", gameUrl)
  83. resp, _ := http.Get(gameUrl)
  84. g := new(game.Game)
  85. b, _ := ioutil.ReadAll(resp.Body)
  86. resp.Body.Close()
  87. json.Unmarshal(b, &g)
  88. boardSummery := g.Board
  89. fmt.Printf("Finish x=%v y=%v\n", boardSummery.Finish.X, boardSummery.Finish.Y)
  90. //join the game
  91. url := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10) + "/join"
  92. jsonStr, _ := json.Marshal(p)
  93. req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
  94. req.Header.Set("Content-Type", "application/json")
  95. client := &http.Client{}
  96. resp, err := client.Do(req)
  97. if err != nil {
  98. panic(err)
  99. }
  100. v, _ := ioutil.ReadAll(resp.Body)
  101. json.Unmarshal(v, p) //Set my location in the player
  102. fmt.Printf("My start position x %v y %v\n", p.Pos.X, p.Pos.Y)
  103. resp.Body.Close()
  104. move(gameId, p, players, boardSummery)
  105. for {
  106. select {
  107. case <-gameEndChan:
  108. fmt.Println("Game ended returning")
  109. close(gameEndChan)
  110. return
  111. case playerUpdate := <-pu:
  112. fmt.Printf("Player update maybe time for a new move\n")
  113. if playerUpdate.GameId == gameId {
  114. players = playerUpdate.Players // TODO fix multi client
  115. for _, pup := range players {
  116. if pup.Id == p.Id {
  117. p.Pos.X = pup.Pos.X
  118. p.Pos.Y = pup.Pos.Y
  119. fmt.Printf("Set my position to x=%v y=%v\n", pup.Pos.X, pup.Pos.Y)
  120. time.Sleep(25 * time.Millisecond)
  121. fmt.Println("Allowed to make a new move")
  122. move(gameId, p, players, boardSummery)
  123. }
  124. }
  125. }
  126. }
  127. }
  128. }
  129. type viewport struct {
  130. x, y, width, height int
  131. Board *board.Board
  132. }
  133. func getViewPort(x, y, width, height int, gameId int64, bs *board.Board) *viewport {
  134. x, y, width, height = board.SanitizeViewPort(bs, x, y, width, height)
  135. url := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10) + "/board?x=" + strconv.Itoa(x) + "&y=" + strconv.Itoa(y) + "&rows=" + strconv.Itoa(height) + "&cols=" + strconv.Itoa(width)
  136. fmt.Println("getViewPort:>", url)
  137. resp, _ := http.Get(url)
  138. board := board.ReadJSON(x, y, resp.Body)
  139. resp.Body.Close()
  140. return &viewport{
  141. x: x,
  142. y: y,
  143. width: width,
  144. height: height,
  145. Board: board,
  146. }
  147. }
  148. func (v viewport) Get(x, y int) board.TileType {
  149. return v.Board.Get(x-v.x, y-v.y)
  150. }
  151. const viewPortWidth = 128
  152. const viewPortHeight = 128
  153. func max(a, b int) int {
  154. if a >= b {
  155. return a
  156. }
  157. return b
  158. }
  159. func min(a, b int) int {
  160. if a <= b {
  161. return a
  162. }
  163. return b
  164. }
  165. func move(gameId int64, p *player.Player, players []*player.Player, bs *board.Board) bool {
  166. fmt.Println("Lets move")
  167. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  168. if bs.Finish.X == p.Pos.X && p.Pos.Y == bs.Finish.Y {
  169. fmt.Println("HELP i'm finished stop me!!")
  170. return false
  171. }
  172. //determine move
  173. viewPortX := 0
  174. viewPortY := 0
  175. if viewPortHeight <= bs.Height || viewPortWidth <= bs.Width {
  176. viewPortX = p.Pos.X - viewPortWidth/2
  177. viewPortY = p.Pos.Y - viewPortHeight/2
  178. }
  179. viewPort := getViewPort(viewPortX, viewPortY, viewPortWidth, viewPortHeight, gameId, bs)
  180. //fmt.Printf("viewport x=%v y=%v width %v height %v viewport board width %v height %v\n", viewPort.x, viewPort.y, viewPort.width, viewPort.height, viewPort.Board.Width, viewPort.Board.Height)
  181. direction := "E"
  182. igloY := (bs.Finish.Y - viewPort.y)
  183. igloYInRange := true
  184. if bs.Finish.Y < viewPort.y {
  185. //iglo ten noorden van viewport
  186. fmt.Println("iglo Y boven")
  187. igloY = 0
  188. igloYInRange = false
  189. /*for viewPort.Get(viewPort.x+viewPortWidth, igloY) == board.Rock && igloY <= viewPort.y+viewPortHeight {
  190. igloY++
  191. }*/
  192. } else if bs.Finish.Y > viewPort.y+viewPort.Board.Height {
  193. //iglo ten zuiden van viewport
  194. fmt.Println("iglo Y onder")
  195. igloY = viewPort.Board.Height - 1
  196. igloYInRange = false
  197. /*for viewPort.Get(viewPort.x+viewPortWidth, igloY) == board.Rock && igloY >= viewPort.y+viewPortHeight {
  198. igloY--
  199. }*/
  200. }
  201. igloX := viewPort.Board.Width-1
  202. igloXInRange := true
  203. if bs.Finish.X < viewPort.x {
  204. //iglo links
  205. fmt.Println("iglo X links")
  206. igloX = 0
  207. igloXInRange = false
  208. } else if bs.Finish.X > viewPort.x+viewPort.Board.Width {
  209. //iglo rechts
  210. fmt.Println("iglo X rechts")
  211. igloX = viewPort.Board.Width - 1
  212. igloXInRange = false
  213. } else if viewPort.x < bs.Finish.X && viewPort.x+viewPort.Board.Width > bs.Finish.X {
  214. // iglo in range
  215. igloX = (bs.Finish.X - viewPort.x)
  216. }
  217. if igloX == viewPort.Board.Width {
  218. fmt.Println("Correting igloX")
  219. igloX--
  220. }
  221. if igloY == viewPort.Board.Height {
  222. fmt.Println("Correting igloY")
  223. igloY--
  224. }
  225. fmt.Printf("Virtuele iglo geplaast op x %v y %v\n", igloX, igloY)
  226. dist := calcDist(igloX, igloY, viewPort)
  227. if !igloXInRange || !igloYInRange {
  228. for igloX > 0 && dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] == 0 {
  229. igloX--
  230. fmt.Printf("replaced iglo to %v x %v\n", igloX, igloY)
  231. dist = calcDist(igloX, igloY, viewPort)
  232. /*
  233. for i := 0; i < viewPort.Board.Height; i++ {
  234. for j := 0; j < viewPort.Board.Width; j++ {
  235. if j == p.Pos.X-viewPort.x && i == p.Pos.Y-viewPort.y {
  236. fmt.Printf("X")
  237. }
  238. if j == igloX && i == igloY {
  239. fmt.Printf("I")
  240. }
  241. if dist[(i*viewPort.Board.Width)+j] < 10 {
  242. fmt.Printf("0")
  243. }
  244. fmt.Printf("%v ", dist[(i*viewPort.Board.Width)+j])
  245. }
  246. fmt.Printf("\n")
  247. }
  248. time.Sleep(1 * time.Second)*/
  249. }
  250. } else {
  251. fmt.Println("Iglo in range lets go!!")
  252. }
  253. smallestDist := math.MaxInt32
  254. if p.Pos.X+1-viewPort.x < viewPort.width && dist[toIndex(p.Pos.X+1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] != 0 {
  255. direction = "E"
  256. smallestDist = dist[toIndex(p.Pos.X+1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)]
  257. }
  258. if p.Pos.X-1-viewPort.x >= 0 && dist[toIndex(p.Pos.X-1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] != 0 && smallestDist > dist[toIndex(p.Pos.X-1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] {
  259. direction = "W"
  260. smallestDist = dist[toIndex(p.Pos.X-1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)]
  261. }
  262. if p.Pos.Y+1-viewPort.y < viewPort.height && dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y+1-viewPort.y, viewPort.Board.Width)] != 0 && smallestDist > dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y+1-viewPort.y, viewPort.Board.Width)] {
  263. direction = "S"
  264. smallestDist = dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y+1-viewPort.y, viewPort.Board.Width)]
  265. }
  266. if p.Pos.Y-1-viewPort.y >= 0 && dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y-1-viewPort.y, viewPort.Board.Width)] != 0 && smallestDist > dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y-1-viewPort.y, viewPort.Board.Width)] {
  267. direction = "N"
  268. smallestDist = dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y-1-viewPort.y, viewPort.Board.Width)]
  269. }
  270. if smallestDist == math.MaxInt32 {
  271. validMoves := make([]string, 0)
  272. if p.Pos.X+1-viewPort.x < viewPort.width && viewPort.Board.Get(p.Pos.X+1-viewPort.x, p.Pos.Y-viewPort.y) != board.Rock {
  273. validMoves = append(validMoves, "E")
  274. validMoves = append(validMoves, "E")
  275. }
  276. if p.Pos.X-1-viewPort.x >= 0 && viewPort.Board.Get(p.Pos.X-1-viewPort.x, p.Pos.Y-viewPort.y) != board.Rock {
  277. validMoves = append(validMoves, "W")
  278. }
  279. if p.Pos.Y+1-viewPort.y < viewPort.height && viewPort.Board.Get(p.Pos.X-viewPort.x, p.Pos.Y+1-viewPort.y) != board.Rock {
  280. validMoves = append(validMoves, "S")
  281. validMoves = append(validMoves, "S")
  282. validMoves = append(validMoves, "S")
  283. }
  284. if p.Pos.Y-1-viewPort.y >= 0 && viewPort.Board.Get(p.Pos.X-viewPort.x, p.Pos.Y-1-viewPort.y) != board.Rock {
  285. validMoves = append(validMoves, "N")
  286. validMoves = append(validMoves, "N")
  287. validMoves = append(validMoves, "N")
  288. }
  289. rInt := r.Intn(len(validMoves))
  290. direction = validMoves[rInt]
  291. }
  292. //send move
  293. url := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10) + "/move/" + p.Id + "/" + direction
  294. fmt.Println("move:>", url, smallestDist, p.Pos.X, p.Pos.Y)
  295. resp, _ := http.Post(url, "text/plain", nil)
  296. resp.Body.Close()
  297. //update x,y
  298. switch direction {
  299. case "N":
  300. p.Pos.Y--
  301. case "E":
  302. p.Pos.X++
  303. case "S":
  304. p.Pos.Y++
  305. case "W":
  306. p.Pos.X--
  307. }
  308. fmt.Printf("new pos x=%v y=%v\n", p.Pos.X, p.Pos.Y)
  309. return true
  310. }
  311. func toXy(index, boardWidth int) (x, y int) {
  312. x = index % boardWidth
  313. y = (index - x) / boardWidth
  314. return x, y
  315. }
  316. func toIndex(x, y, boardWidth int) (index int) {
  317. return y*boardWidth + x
  318. }
  319. func calcDist(igloX, igloY int, viewPort *viewport) map[int]int {
  320. distance := make(map[int]int)
  321. s := NewQueue()
  322. index := toIndex(igloX, igloY, viewPort.Board.Width)
  323. s.Push(index)
  324. distance[index] = -1
  325. for s.Len() != 0 {
  326. i := s.Poll()
  327. x, y := toXy(i, viewPort.Board.Width)
  328. if x+1 < viewPort.Board.Width {
  329. newI := toIndex(x+1, y, viewPort.Board.Width)
  330. if distance[newI] == 0 && viewPort.Board.Get(x+1, y) != board.Rock {
  331. distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1
  332. s.Push(newI)
  333. }
  334. }
  335. if x-1 >= 0 {
  336. newI := toIndex(x-1, y, viewPort.Board.Width)
  337. if distance[newI] == 0 && viewPort.Board.Get(x-1, y) != board.Rock {
  338. distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1
  339. s.Push(newI)
  340. }
  341. }
  342. if y+1 < viewPort.Board.Height {
  343. newI := toIndex(x, y+1, viewPort.Board.Width)
  344. if distance[newI] == 0 && viewPort.Board.Get(x, y+1) != board.Rock {
  345. distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1
  346. s.Push(newI)
  347. }
  348. }
  349. if y-1 >= 0 {
  350. newI := toIndex(x, y-1, viewPort.Board.Width)
  351. if distance[newI] == 0 && viewPort.Board.Get(x, y-1) != board.Rock {
  352. distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1
  353. s.Push(newI)
  354. }
  355. }
  356. }
  357. return distance
  358. }
  359. /*
  360. type stack []int
  361. func (s stack) Empty() bool { return len(s) == 0 }
  362. func (s stack) Peek() int { return s[len(s)-1] }
  363. func (s *stack) Put(i int) { (*s) = append((*s), i) }
  364. func (s *stack) Pop() int {
  365. d := (*s)[len(*s)-1]
  366. (*s) = (*s)[:len(*s)-1]
  367. return d
  368. }
  369. */
  370. type queuenode struct {
  371. data int
  372. next *queuenode
  373. }
  374. // A go-routine safe FIFO (first in first out) data stucture.
  375. type Queue struct {
  376. head *queuenode
  377. tail *queuenode
  378. count int
  379. }
  380. // Creates a new pointer to a new queue.
  381. func NewQueue() *Queue {
  382. q := &Queue{}
  383. return q
  384. }
  385. // Returns the number of elements in the queue (i.e. size/length)
  386. // go-routine safe.
  387. func (q *Queue) Len() int {
  388. return q.count
  389. }
  390. // Pushes/inserts a value at the end/tail of the queue.
  391. // Note: this function does mutate the queue.
  392. // go-routine safe.
  393. func (q *Queue) Push(item int) {
  394. n := &queuenode{data: item}
  395. if q.tail == nil {
  396. q.tail = n
  397. q.head = n
  398. } else {
  399. q.tail.next = n
  400. q.tail = n
  401. }
  402. q.count++
  403. }
  404. // Returns the value at the front of the queue.
  405. // i.e. the oldest value in the queue.
  406. // Note: this function does mutate the queue.
  407. // go-routine safe.
  408. func (q *Queue) Poll() int {
  409. n := q.head
  410. q.head = n.next
  411. if q.head == nil {
  412. q.tail = nil
  413. }
  414. q.count--
  415. return n.data
  416. }
  417. // Returns a read value at the front of the queue.
  418. // i.e. the oldest value in the queue.
  419. // Note: this function does NOT mutate the queue.
  420. // go-routine safe.
  421. func (q *Queue) Peek() int {
  422. n := q.head
  423. return n.data
  424. }
  425. func playerJoin(p *player.Player) {
  426. }
  427. func initLogging() {
  428. logFile, err := os.Create("server.log")
  429. if err == nil {
  430. log.SetOutput(logFile)
  431. } else {
  432. log.Println("ERROR: Cannot open log file, using console.")
  433. log.Printf("%v=n", err)
  434. }
  435. }