package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "math" "math/rand" "net/http" "os" "strconv" "time" "battlecamp-go/board" "battlecamp-go/events" "battlecamp-go/flag" "battlecamp-go/game" "battlecamp-go/player" "battlecamp-go/stomp" ) func main() { initLogging() log.Println("Game bot version 0.1") flag.ParseFlags() pu := make(chan *events.PlayerUpdate) go subscribeToUpdate(pu) for { subscribeToGame(pu) } } func subscribeToGame(pu chan *events.PlayerUpdate) { sub := stomp.Subscribe("game") gameEndChannels := make(map[int]*[]chan bool) for { announcement := <-sub gs := new(events.GameStart) json.Unmarshal(announcement.Body, &gs) fmt.Printf("announcement type: %v for game %v\n", gs.Type, strconv.FormatInt(gs.GameId, 10)) if "GAME_START" == gs.Type { names := []string{"Zeus"} tmp := make([]chan bool, 0, len(names)) gameEndChannels[int(gs.GameId)] = &tmp for _, name := range names { gameEndChan := make(chan bool) tmp := append(*gameEndChannels[int(gs.GameId)], gameEndChan) gameEndChannels[int(gs.GameId)] = &tmp go joinGame(gs.GameId, gameEndChan, pu, name) } } else { chans := gameEndChannels[int(gs.GameId)] for _, channel := range *chans { channel <- true } } } } func subscribeToUpdate(pu chan *events.PlayerUpdate) { sub := stomp.Subscribe("update") for { announcement := <-sub if "vnd.battlecamp.player" == announcement.ContentType { p := new(player.Player) json.Unmarshal(announcement.Body, &p) playerJoin(p) } else { pue := new(events.PlayerUpdate) json.Unmarshal(announcement.Body, &pue) pu <- pue } } } func joinGame(gameId int64, gameEndChan chan bool, pu chan *events.PlayerUpdate, name string) { //join game p := &player.Player{ Id: name, Color: "#238b02", Type: 1, } players := make([]*player.Player, 5) //retrieve finish gameUrl := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10) fmt.Println("getGame:>", gameUrl) resp, _ := http.Get(gameUrl) g := new(game.Game) b, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() json.Unmarshal(b, &g) boardSummery := g.Board fmt.Printf("Finish x=%v y=%v\n", boardSummery.Finish.X, boardSummery.Finish.Y) //join the game url := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10) + "/join" jsonStr, _ := json.Marshal(p) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { panic(err) } v, _ := ioutil.ReadAll(resp.Body) json.Unmarshal(v, p) //Set my location in the player fmt.Printf("My start position x %v y %v\n", p.Pos.X, p.Pos.Y) resp.Body.Close() move(gameId, p, players, boardSummery) for { select { case <-gameEndChan: fmt.Println("Game ended returning") close(gameEndChan) return case playerUpdate := <-pu: if playerUpdate.GameId == gameId { players = playerUpdate.Players // TODO fix multi client for _, pup := range players { if pup.Id == p.Id { p.Pos.X = pup.Pos.X p.Pos.Y = pup.Pos.Y fmt.Printf("Set my position to x=%v y=%v\n", pup.Pos.X, pup.Pos.Y) time.Sleep(25 * time.Millisecond) fmt.Println("Allowed to make a new move") move(gameId, p, players, boardSummery) } } } } } } type viewport struct { x, y, width, height int Board *board.Board } func getViewPort(x, y, width, height int, gameId int64, bs *board.Board) *viewport { x, y, width, height = board.SanitizeViewPort(bs, x, y, width, height) 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) fmt.Println("getViewPort:>", url) resp, _ := http.Get(url) board := board.ReadJSON(x, y, resp.Body) resp.Body.Close() return &viewport{ x: x, y: y, width: width, height: height, Board: board, } } func (v viewport) Get(x, y int) board.TileType { return v.Board.Get(x-v.x, y-v.y) } const viewPortWidth = 128 const viewPortHeight = 128 func max(a, b int) int { if a >= b { return a } return b } func min(a, b int) int { if a <= b { return a } return b } func move(gameId int64, p *player.Player, players []*player.Player, bs *board.Board) bool { fmt.Println("Lets move") r := rand.New(rand.NewSource(time.Now().UnixNano())) if bs.Finish.X == p.Pos.X && p.Pos.Y == bs.Finish.Y { fmt.Println("HELP i'm finished stop me!!") return false } //determine move viewPortX := 0 viewPortY := 0 if viewPortHeight <= bs.Height || viewPortWidth <= bs.Width { viewPortX = p.Pos.X - viewPortWidth/2 viewPortY = p.Pos.Y - viewPortHeight/2 } viewPort := getViewPort(viewPortX, viewPortY, viewPortWidth, viewPortHeight, gameId, bs) //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) direction := "E" igloY := bs.Finish.Y - viewPort.y igloYInRange := true if bs.Finish.Y < viewPort.y { //iglo ten noorden van viewport fmt.Println("iglo Y boven") igloY = 0 igloYInRange = false /*for viewPort.Get(viewPort.x+viewPortWidth, igloY) == board.Rock && igloY <= viewPort.y+viewPortHeight { igloY++ }*/ } else if bs.Finish.Y > viewPort.y+viewPort.Board.Height { //iglo ten zuiden van viewport fmt.Println("iglo Y onder") igloY = viewPort.Board.Height - 1 igloYInRange = false /*for viewPort.Get(viewPort.x+viewPortWidth, igloY) == board.Rock && igloY >= viewPort.y+viewPortHeight { igloY-- }*/ } igloX := viewPort.Board.Width igloXInRange := true if bs.Finish.X < viewPort.x { //iglo links fmt.Println("iglo X links") igloX = 0 igloXInRange = false } else if bs.Finish.X > viewPort.x+viewPort.Board.Width { //iglo rechts fmt.Println("iglo X rechts") igloX = viewPort.Board.Width - 1 igloXInRange = false } else if viewPort.x < bs.Finish.X && viewPort.x+viewPort.Board.Width > bs.Finish.X { // iglo in range igloX = (bs.Finish.X - viewPort.x) } fmt.Printf("Virtuele iglo geplaast op x %v y %v\n", igloX, igloY) dist := calcDist(igloX, igloY, viewPort) if !igloXInRange || !igloYInRange { for igloX > 100 && dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] == 0 { igloX-- fmt.Printf("replaced iglo to %v x %v\n", igloX, igloY) dist = calcDist(igloX, igloY, viewPort) /* for i := 0; i < viewPort.Board.Height; i++ { for j := 0; j < viewPort.Board.Width; j++ { if j == p.Pos.X-viewPort.x && i == p.Pos.Y-viewPort.y { fmt.Printf("X") } if j == igloX && i == igloY { fmt.Printf("I") } if dist[(i*viewPort.Board.Width)+j] < 10 { fmt.Printf("0") } fmt.Printf("%v ", dist[(i*viewPort.Board.Width)+j]) } fmt.Printf("\n") } time.Sleep(1 * time.Second)*/ } } else { fmt.Println("Iglo in range lets go!!") } smallestDist := math.MaxInt32 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 { direction = "E" smallestDist = dist[toIndex(p.Pos.X+1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] } 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)] { direction = "W" smallestDist = dist[toIndex(p.Pos.X-1-viewPort.x, p.Pos.Y-viewPort.y, viewPort.Board.Width)] } 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)] { direction = "S" smallestDist = dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y+1-viewPort.y, viewPort.Board.Width)] } 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)] { direction = "N" smallestDist = dist[toIndex(p.Pos.X-viewPort.x, p.Pos.Y-1-viewPort.y, viewPort.Board.Width)] } if smallestDist == math.MaxInt32 { validMoves := make([]string, 0) 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 { validMoves = append(validMoves, "E") validMoves = append(validMoves, "E") } if p.Pos.X-1-viewPort.x >= 0 && viewPort.Board.Get(p.Pos.X-1-viewPort.x, p.Pos.Y-viewPort.y) != board.Rock { validMoves = append(validMoves, "W") } 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 { validMoves = append(validMoves, "S") validMoves = append(validMoves, "S") validMoves = append(validMoves, "S") } if p.Pos.Y-1-viewPort.y >= 0 && viewPort.Board.Get(p.Pos.X-viewPort.x, p.Pos.Y-1-viewPort.y) != board.Rock { validMoves = append(validMoves, "N") validMoves = append(validMoves, "N") validMoves = append(validMoves, "N") } rInt := r.Intn(len(validMoves)) direction = validMoves[rInt] } //send move url := "http://localhost:8080/games/" + strconv.FormatInt(gameId, 10) + "/move/" + p.Id + "/" + direction fmt.Println("move:>", url, smallestDist, p.Pos.X, p.Pos.Y) resp, _ := http.Post(url, "text/plain", nil) resp.Body.Close() //update x,y switch direction { case "N": p.Pos.Y-- case "E": p.Pos.X++ case "S": p.Pos.Y++ case "W": p.Pos.X-- } fmt.Printf("new pos x=%v y=%v\n", p.Pos.X, p.Pos.Y) return true } func toXy(index, boardWidth int) (x, y int) { x = index % boardWidth y = (index - x) / boardWidth return x, y } func toIndex(x, y, boardWidth int) (index int) { return y*boardWidth + x } func calcDist(igloX, igloY int, viewPort *viewport) map[int]int { distance := make(map[int]int) s := NewQueue() index := toIndex(igloX, igloY, viewPort.Board.Width) s.Push(index) distance[index] = -1 for s.Len() != 0 { i := s.Poll() x, y := toXy(i, viewPort.Board.Width) if x+1 < viewPort.Board.Width { newI := toIndex(x+1, y, viewPort.Board.Width) if distance[newI] == 0 && viewPort.Board.Get(x+1, y) != board.Rock { distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1 s.Push(newI) } } if x-1 >= 0 { newI := toIndex(x-1, y, viewPort.Board.Width) if distance[newI] == 0 && viewPort.Board.Get(x-1, y) != board.Rock { distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1 s.Push(newI) } } if y+1 < viewPort.Board.Height { newI := toIndex(x, y+1, viewPort.Board.Width) if distance[newI] == 0 && viewPort.Board.Get(x, y+1) != board.Rock { distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1 s.Push(newI) } } if y-1 >= 0 { newI := toIndex(x, y-1, viewPort.Board.Width) if distance[newI] == 0 && viewPort.Board.Get(x, y-1) != board.Rock { distance[newI] = max(distance[toIndex(x, y, viewPort.Board.Width)], 0) + 1 s.Push(newI) } } } return distance } /* type stack []int func (s stack) Empty() bool { return len(s) == 0 } func (s stack) Peek() int { return s[len(s)-1] } func (s *stack) Put(i int) { (*s) = append((*s), i) } func (s *stack) Pop() int { d := (*s)[len(*s)-1] (*s) = (*s)[:len(*s)-1] return d } */ type queuenode struct { data int next *queuenode } // A go-routine safe FIFO (first in first out) data stucture. type Queue struct { head *queuenode tail *queuenode count int } // Creates a new pointer to a new queue. func NewQueue() *Queue { q := &Queue{} return q } // Returns the number of elements in the queue (i.e. size/length) // go-routine safe. func (q *Queue) Len() int { return q.count } // Pushes/inserts a value at the end/tail of the queue. // Note: this function does mutate the queue. // go-routine safe. func (q *Queue) Push(item int) { n := &queuenode{data: item} if q.tail == nil { q.tail = n q.head = n } else { q.tail.next = n q.tail = n } q.count++ } // Returns the value at the front of the queue. // i.e. the oldest value in the queue. // Note: this function does mutate the queue. // go-routine safe. func (q *Queue) Poll() int { n := q.head q.head = n.next if q.head == nil { q.tail = nil } q.count-- return n.data } // Returns a read value at the front of the queue. // i.e. the oldest value in the queue. // Note: this function does NOT mutate the queue. // go-routine safe. func (q *Queue) Peek() int { n := q.head return n.data } func playerJoin(p *player.Player) { } func initLogging() { logFile, err := os.Create("server.log") if err == nil { log.SetOutput(logFile) } else { log.Println("ERROR: Cannot open log file, using console.") log.Printf("%v=n", err) } }