func fightN(f1, f2 Factory, n int) []MatchResult { aiA := &aiAdapter{factory: f1, games: make(map[string]gameState)} aiB := &aiAdapter{factory: f2, games: make(map[string]gameState)} a1, a2 := net.Pipe() b1, b2 := net.Pipe() ca1, ca2 := rpc.StreamTransport(a1), rpc.StreamTransport(a2) cb1, cb2 := rpc.StreamTransport(b1), rpc.StreamTransport(b2) // Server-side srvA := botapi.Ai_ServerToClient(aiA) srvB := botapi.Ai_ServerToClient(aiB) serverConnA := rpc.NewConn(ca1, rpc.MainInterface(srvA.Client)) serverConnB := rpc.NewConn(cb1, rpc.MainInterface(srvB.Client)) defer serverConnA.Wait() defer serverConnB.Wait() // Client-side ctx := context.Background() clientConnA := rpc.NewConn(ca2) clientConnB := rpc.NewConn(cb2) defer clientConnA.Close() defer clientConnB.Close() clientA := localAI{botapi.Ai{Client: clientConnA.Bootstrap(ctx)}} clientB := localAI{botapi.Ai{Client: clientConnB.Bootstrap(ctx)}} matchRes := make([]MatchResult, n) // Run the game for i := 0; i < n; i++ { b := engine.EmptyBoard(engine.DefaultConfig) b.InitBoard(engine.DefaultConfig) for !b.IsFinished() { turnCtx, _ := context.WithTimeout(ctx, 30*time.Second) resA, _ := clientA.takeTurn(turnCtx, strconv.Itoa(i), b, engine.P1Faction) resB, _ := clientB.takeTurn(turnCtx, strconv.Itoa(i), b, engine.P2Faction) b.Update(resA, resB) } matchRes[i] = MatchResult{ P1Score: b.BotCount(1), P2Score: b.BotCount(2), } } return matchRes }
func runMatch(gidCh chan<- gameID, ctx gocontext.Context, ds datastore, aiA, aiB *onlineAI, bc engine.BoardConfig) error { sTime := time.Now() // Create new board and store it. b := engine.EmptyBoard(bc) b.InitBoard(bc) _, seg, _ := capnp.NewMessage(capnp.SingleSegment(nil)) wb, _ := botapi.NewRootInitialBoard(seg) b.ToWireWithInitial(wb, engine.P1Faction) gid, err := ds.startGame(aiA.Info.ID, aiB.Info.ID, wb) if err != nil { return err } gidCh <- gid // Run the game for !b.IsFinished() { turnCtx, _ := gocontext.WithTimeout(ctx, 30*time.Second) chA, chB := make(chan turnResult), make(chan turnResult) go aiA.takeTurn(turnCtx, gid, b, engine.P1Faction, chA) go aiB.takeTurn(turnCtx, gid, b, engine.P2Faction, chB) ra, rb := <-chA, <-chB if ra.err.HasError() { log.Printf("Errors from AI ID %s: %v", aiA.Info.ID, ra.err) } if rb.err.HasError() { log.Printf("Errors from AI ID %s: %v", aiB.Info.ID, rb.err) } b.Update(ra.results, rb.results) _, s, err := capnp.NewMessage(capnp.SingleSegment(nil)) if err != nil { return err } r, err := botapi.NewRootReplay_Round(s) if err != nil { return err } wireBoard, err := r.NewEndBoard() if err != nil { return err } b.ToWire(wireBoard, engine.P1Faction) ral, rbl := ra.results.Len(), rb.results.Len() turns, err := botapi.NewTurn_List(r.Segment(), int32(ral+rbl)) if err != nil { return err } for i := 0; i < ral; i++ { t := ra.results.At(i) if err := turns.Set(i, t); err != nil { return err } } for i := 0; i < rbl; i++ { t := rb.results.At(i) if err := turns.Set(i+ral, t); err != nil { return err } } r.SetMoves(turns) db.addRound(gid, r) } gInfo := &gameInfo{ ID: gid, AI1: &aiA.Info, AI2: &aiB.Info, AI1Score: b.BotCount(1), AI2Score: b.BotCount(2), StartTime: sTime, EndTime: time.Now(), } return db.finishGame(gid, &aiA.Info, &aiB.Info, gInfo) }