天下一 Game Battle Contest 2021 Autumn

天下一 Game Battle Contest 2021 Autumn
天下一 Game Battle Contest 2021 Autumnに参加した記録です。
Go言語で参加しました。
/* 実行には go 環境が必要です。 TOKEN 変数を書き換えて実行してください。 サンプル初期実装 移動先に資源が無い回収車を、ランダムに選んだ出現中の資源へと移動させる ただしこのとき2台以上の回収車が同じ資源を選ばないようにする */ package main import ( "encoding/json" "fmt" "io/ioutil" "log" "math" "math/rand" "net/http" "os" "sort" "strings" "time" ) // GameServer ゲームサーバのアドレスとトークン // var GameServer string = "https://contest.2021-autumn.gbc.tenka1.klab.jp" var GameServer string = "https://contest.2021-autumn.gbc.tenka1.klab.jp/staging" // 開発用環境 var TOKEN string = "Your Token" func init() { rand.Seed(time.Now().Unix()) if os.Getenv("GAME_SERVER") != "" { GameServer = os.Getenv("GAME_SERVER") } if os.Getenv("TOKEN") != "" { TOKEN = os.Getenv("TOKEN") } } type AgentMove struct { X float64 `json:"x"` Y float64 `json:"y"` T int `json:"t"` } type Agent struct { Move []AgentMove `json:"move"` } type Resource struct { ID int `json:"id"` X int `json:"x"` Y int `json:"y"` T0 int `json:"t0"` T1 int `json:"t1"` Type string `json:"type"` Weight int `json:"weight"` } type ResourceWithAmount struct { Resource Amount float64 `json:"amount"` } type OwnedResource struct { Type string `json:"type"` Amount float64 `json:"amount"` } type Game struct { Status string `json:"status"` Now int `json:"now"` Agent []Agent `json:"agent"` Resource []Resource `json:"resource"` NextResource int `json:"next_resource"` OwnedResource []OwnedResource `json:"owned_resource"` } type Move struct { Status string `json:"status"` Now int `json:"now"` Move []AgentMove `json:"move"` } type Resources struct { Status string `json:"status"` Resource []ResourceWithAmount `json:"resource"` } func callAPI(x string) ([]byte, error) { url := GameServer + x fmt.Println(url) resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { return nil, fmt.Errorf(resp.Status) } return body, err } func callGame() (*Game, error) { res, err := callAPI(fmt.Sprintf("/api/game/%s", TOKEN)) if err != nil { return nil, err } var game Game err = json.Unmarshal(res, &game) return &game, err } func callMove(index int, x int, y int) (*Move, error) { res, err := callAPI(fmt.Sprintf("/api/move/%s/%d-%d-%d", TOKEN, index, x, y)) if err != nil { return nil, err } var move Move err = json.Unmarshal(res, &move) return &move, err } func callWillMove(index int, x int, y int, t int) (*Move, error) { res, err := callAPI(fmt.Sprintf("/api/will_move/%s/%d-%d-%d-%d", TOKEN, index, x, y, t)) if err != nil { return nil, err } var move Move err = json.Unmarshal(res, &move) return &move, err } func callResources(ids []int) (*Resources, error) { var strIds []string for _, id := range ids { strIds = append(strIds, fmt.Sprint(id)) } res, err := callAPI(fmt.Sprintf("/api/resources/%s/%s", TOKEN, strings.Join(strIds, "-"))) if err != nil { return nil, err } var resources Resources err = json.Unmarshal(res, &resources) return &resources, err } func calcScore(game *Game) float64 { a := []float64{} for _, o := range game.OwnedResource { a = append(a, o.Amount) } sort.Float64s(a) return a[0] + 0.1*a[1] + 0.01*a[2] } type Bot struct { } func NewBot() *Bot { return &Bot{} } var GameCached *Game var Now float64 var Next_API float64 func (bot *Bot) solve() { for { if Now >= Next_API { game, err := callGame() if err != nil { log.Fatal(err) } if game.Status != "ok" { log.Fatal(game.Status) } GameCached = game } var want_type string = GameCached.OwnedResource[0].Type var tmp OwnedResource = GameCached.OwnedResource[0] for _, o := range GameCached.OwnedResource { fmt.Printf("%s: %.2f ", o.Type, o.Amount) if tmp.Amount > o.Amount { want_type = o.Type tmp = o } } fmt.Printf("Score: %.2f\n", calcScore(GameCached)) type Point struct { X int Y int } resourcePositions := map[Point]Resource{} resourcePositionsT := map[Point]bool{} resourcePositions_wanted := map[Point]Resource{} for _, r := range GameCached.Resource { if r.T0 <= GameCached.Now && GameCached.Now < r.T1 { resourcePositions[Point{X: r.X, Y: r.Y}] = r resourcePositionsT[Point{X: r.X, Y: r.Y}] = true if r.Type == want_type { resourcePositions_wanted[Point{X: r.X, Y: r.Y}] = r } } } var indexList []int for index := 1; index <= 5; index++ { agentMove := GameCached.Agent[index-1].Move m := agentMove[len(agentMove)-1] p := Point{X: int(m.X), Y: int(m.Y)} if resourcePositionsT[p] { delete(resourcePositions, p) delete(resourcePositionsT, p) ng, err := callMove(index, int(m.X), int(m.Y+0.01)) if err != nil { log.Fatal(err) } GameCached.Agent[index-1].Move = append(GameCached.Agent[index-1].Move, ng.Move...) } else { indexList = append(indexList, index) } } for _, index := range indexList { if len(resourcePositionsT) == 0 { break } agentMove := GameCached.Agent[index-1].Move m := agentMove[len(agentMove)-1] ans := Point{X: 100, Y: 100} ans1 := Point{X: 100, Y: 100} for p := range resourcePositions { if math.Sqrt(math.Pow(m.X-float64(ans.X), 2.0)+math.Pow(m.Y-float64(ans.Y), 2.0)) > math.Sqrt(math.Pow((m.X-float64(p.X)), 2.0)+math.Pow((m.Y-float64(p.Y)), 2.0)) && float64(resourcePositions[p].T1) >= float64(GameCached.Now)+math.Sqrt(math.Pow((m.X-float64(p.X)), 2.0)+math.Pow((m.Y-float64(p.Y)), 2.0)) { ans = p } } for p := range resourcePositions_wanted { if math.Sqrt(math.Pow(m.X-float64(ans1.X), 2.0)+math.Pow(m.Y-float64(ans1.Y), 2.0)) > math.Sqrt(math.Pow((m.X-float64(p.X)), 2.0)+math.Pow((m.Y-float64(p.Y)), 2.0)) && float64(resourcePositions[p].T1) >= float64(m.T)+math.Sqrt(math.Pow((m.X-float64(p.X)), 2.0)+math.Pow((m.Y-float64(p.Y)), 2.0)) { ans1 = p } } if (ans1 != Point{X: 100, Y: 100}) { ng, err := callWillMove(index, ans1.X, ans1.Y, m.T) GameCached.Agent[index-1].Move = append(GameCached.Agent[index-1].Move, ng.Move...) GameCached.Agent[index-1].Move[len(agentMove)-1].T = int(float64(m.T) + math.Sqrt(math.Pow((m.X-float64(ans1.X)), 2.0)+math.Pow((m.Y-float64(ans1.Y)), 2.0))) if err != nil { log.Fatal(err) } delete(resourcePositions, ans1) delete(resourcePositionsT, ans1) delete(resourcePositions_wanted, ans1) continue } if (ans != Point{X: 100, Y: 100}) { ng, err := callWillMove(index, ans.X, ans.Y, m.T) GameCached.Agent[index-1].Move = append(GameCached.Agent[index-1].Move, ng.Move...) GameCached.Agent[index-1].Move[len(agentMove)-1].T = int(float64(m.T) + math.Sqrt(math.Pow((m.X-float64(ans.X)), 2.0)+math.Pow((m.Y-float64(ans.Y)), 2.0))) if err != nil { log.Fatal(err) } delete(resourcePositions, ans) delete(resourcePositionsT, ans) continue } else { continue } } // time.Sleep(100 * time.Millisecond) Now += Now + 100 } } func main() { game, err := callGame() if err != nil { log.Fatal(err) } if game.Status != "ok" { log.Fatal(game.Status) } Now = float64(game.Now) Next_API = Now + float64(game.NextResource) GameCached = game bot := NewBot() bot.solve() }
やったこと
0. 5台全部動いていなかったから、少しだけ動かして必ず動くようにした。
1. game/api が呼ばれるのが、1000 ミリ秒に一回程度だったのでGameをキャッシュしてAPIを叩かないようにしようとした。(正常に動いていたかは不明)
2. 全体として、amoutの小さいフラグを取りに行くようにした。
3. 基本的にすべての動作をcallWillMoveで動かすようにした
for index := 1; index <= 5; index++ { agentMove := GameCached.Agent[index-1].Move m := agentMove[len(agentMove)-1] p := Point{X: int(m.X), Y: int(m.Y)} if resourcePositionsT[p] { delete(resourcePositions, p) delete(resourcePositionsT, p) ng, err := callMove(index, int(m.X), int(m.Y+0.01)) if err != nil { log.Fatal(err) } GameCached.Agent[index-1].Move = append(GameCached.Agent[index-1].Move, ng.Move...) } else { indexList = append(indexList, index) } }
var GameCached *Game var Now float64 var Next_API float64 func (bot *Bot) solve() { for { if Now >= Next_API { game, err := callGame() if err != nil { log.Fatal(err) } if game.Status != "ok" { log.Fatal(game.Status) } GameCached = game }
ng, err := callWillMove(index, ans1.X, ans1.Y, m.T) GameCached.Agent[index-1].Move = append(GameCached.Agent[index-1].Move, ng.Move...) GameCached.Agent[index-1].Move[len(agentMove)-1].T = int(float64(m.T) + math.Sqrt(math.Pow((m.X-float64(ans1.X)), 2.0)+math.Pow((m.Y-float64(ans1.Y)), 2.0))) if err != nil { log.Fatal(err)
ng, err := callWillMove(index, ans.X, ans.Y, m.T)