From 942f629bd50e2b5ca697cfb1079e4bc67b827171 Mon Sep 17 00:00:00 2001 From: RobolabGs2 Date: Sat, 10 Oct 2020 02:19:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=80=D0=B0=D1=83=D0=BD=D0=B4=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B3=D1=83=D0=BB=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=BE=D0=B2=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B7=20=D1=84=D0=BB=D0=B0=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++++- main.go | 107 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 102 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 79a5ef5..540c677 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,18 @@ CLI утилита для проведения соревнований межд + `stdin` принимает ходы соперника + `stdout` пишет свой ход + `stderr` пишет всё остальное - логи, информацию для человека, состояние доски ++ `exit code`: победа-`0`, поражение-`1`, ничья-`2` # Использование -`botctl path/to/mybot1.exe path/to/mybot2.exe` +``` +Usage: + botctl [flags] path/to/mybot1.exe path/to/mybot2.exe + -r int + Количество раундов (default 1) + -v int + 0 - логи ботов не выводятся, 1 - выводятся логи первого, 2 - обоих ботов (default 1) +``` -Порядок ботов влияет на очерёдность хода, в результате будет выводиться `stderr` первого бота \ No newline at end of file +Порядок ботов влияет на вывод: в `stdout` будет писаться `stderr` первого бота, в `stderr` - второго. + +По окончанию всех раундов будет выведен суммарный счёт. При смене раунда цвета меняются. \ No newline at end of file diff --git a/main.go b/main.go index 849ff42..83dd439 100644 --- a/main.go +++ b/main.go @@ -1,34 +1,107 @@ package main import ( + "errors" + "flag" + "fmt" "log" "os" "os/exec" + "path/filepath" + "strconv" ) func main() { - if (len(os.Args)) != 3 { - log.Fatalf("bot-connector [bot1].exe [bot2].exe") - } - bot1ExeName := os.Args[1] - bot2ExeName := os.Args[2] - bot1, bot2 := MakeCmd(bot1ExeName, "0"), MakeCmd(bot2ExeName, "1") - bot1.Stdout, _ = bot2.StdinPipe() - bot2.Stdout, _ = bot1.StdinPipe() - bot1.Stderr = os.Stdout - logAndExitOnError(bot1.Start(), bot1ExeName) - logAndExitOnError(bot2.Start(), bot2ExeName) - logAndExitOnError(bot1.Wait(), bot1ExeName) - logAndExitOnError(bot2.Wait(), bot2ExeName) + rounds := flag.Int("r", 1, "Количество раундов") + verbosity := flag.Int("v", 1, "0 - логи ботов не выводятся, 1 - выводятся логи первого, 2 - обоих ботов") + flag.Parse() + if flag.NArg() != 2 { + _, _ = fmt.Fprintln(os.Stderr, "Usage:\n botctl [flags] path/to/mybot1.exe path/to/mybot2.exe") + flag.PrintDefaults() + return + } + bot1ExeName := filepath.Clean(flag.Arg(0)) + bot2ExeName := filepath.Clean(flag.Arg(1)) + checkFile(bot1ExeName) + checkFile(bot2ExeName) + totalScore1, totalScore2 := GameResult(0), GameResult(0) + for i := 0; i < *rounds; i++ { + bot1, bot2 := MakeCmd(bot1ExeName, i%2), MakeCmd(bot2ExeName, 1-i%2) + bot1.Stdout, _ = bot2.StdinPipe() + bot2.Stdout, _ = bot1.StdinPipe() + switch *verbosity { + case 2: + bot2.Stderr = os.Stderr + fallthrough + case 1: + bot1.Stderr = os.Stdout + } + log.Println("Раунд:", i) + logAndExitOnError(bot1.Start(), bot1ExeName) + logAndExitOnErrorAndAction(bot2.Start(), bot2ExeName, bot1.Process.Kill) + score1, err := SummarizeGame(bot1.Wait()) + if err != nil { + log.Println(bot1ExeName, ": ", err) + } + score2, err := SummarizeGame(bot2.Wait()) + if err != nil { + log.Println(bot2ExeName, ": ", err) + } + if score1+score2 != Draw { + log.Println("Кто-то из ботов мухлюет") + } + totalScore1 += score1 + totalScore2 += score2 + log.Println(score1, ":", score2) + } + if *rounds > 1 { + log.Println("Итого:") + log.Println(totalScore1, ":", totalScore2) + } +} + +func checkFile(filename string) { + if _, err := os.Stat(filename); err != nil { + log.Fatalln("Проблемы с ", filename, ":", err) + } } -func logAndExitOnError(err error, bot1ExeName string) { +func logAndExitOnError(err error, botExeName string) { + if err != nil { + log.Fatalln(botExeName, err) + } +} +func logAndExitOnErrorAndAction(err error, botExeName string, action func() error) { if err != nil { - log.Fatalln(bot1ExeName, err) + _ = action() + log.Fatalln(botExeName, err) + } +} + +type GameResult int + +const ( + Win GameResult = 1 + Lose GameResult = -1 + Draw GameResult = 0 +) + +func SummarizeGame(err error) (GameResult, error) { + if err == nil { + return Win, nil + } + if exit := new(exec.ExitError); errors.As(err, &exit) { + switch exit.ExitCode() { + case 1: + return Lose, nil + case 2: + return Draw, nil + } } + return 0, err } -func MakeCmd(botExeName string, order string) *exec.Cmd { +func MakeCmd(botExeName string, order int) *exec.Cmd { // Платформозависимо - запускаем с помощью cmd - return exec.Command("cmd", "/C", botExeName, order) + return exec.Command("cmd", "/C", botExeName, strconv.Itoa(order)) }