Amedeo
SYMBIOSYS OF
PURPOSE & STYLE
Follow us

Search

ekusiadadus
  -  Programming   -  Competitive Programming   -  天下一 Game Battle Contest 2021 Autumn
天下一 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)
Leave a Comment