From 45490f3d1eec0f61051378dc6e251ccc7270f763 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Mon, 17 Feb 2025 08:20:35 -0500 Subject: [PATCH] feat(parser/tierscore): new tiered scoring parser --- internal/parser/all.go | 1 + internal/parser/tierscore/meta.go | 24 +++++++ internal/parser/tierscore/parser.go | 102 ++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 internal/parser/tierscore/meta.go create mode 100644 internal/parser/tierscore/parser.go diff --git a/internal/parser/all.go b/internal/parser/all.go index 9b511d4..529f917 100644 --- a/internal/parser/all.go +++ b/internal/parser/all.go @@ -13,6 +13,7 @@ import ( _ "github.com/joint-online-judge/JOJ3/internal/parser/resultdetail" _ "github.com/joint-online-judge/JOJ3/internal/parser/resultstatus" _ "github.com/joint-online-judge/JOJ3/internal/parser/sample" + _ "github.com/joint-online-judge/JOJ3/internal/parser/tierscore" ) // this file does nothing but imports to ensure all the init() functions diff --git a/internal/parser/tierscore/meta.go b/internal/parser/tierscore/meta.go new file mode 100644 index 0000000..2b1528c --- /dev/null +++ b/internal/parser/tierscore/meta.go @@ -0,0 +1,24 @@ +// Package tierscore provides a parser for tiered scoring based on +// time and memory constraints. Leave the field empty or 0 to disable. + +package tierscore + +import "github.com/joint-online-judge/JOJ3/internal/stage" + +var name = "tierscore" + +type Tier struct { + TimeLessThan uint64 // ns + MemoryLessThan uint64 // bytes + Score int +} + +type Conf struct { + Tiers []Tier +} + +type TierScore struct{} + +func init() { + stage.RegisterParser(name, &TierScore{}) +} diff --git a/internal/parser/tierscore/parser.go b/internal/parser/tierscore/parser.go new file mode 100644 index 0000000..e9e0c00 --- /dev/null +++ b/internal/parser/tierscore/parser.go @@ -0,0 +1,102 @@ +package tierscore + +import ( + "fmt" + "strings" + + "github.com/joint-online-judge/JOJ3/internal/stage" +) + +func (*TierScore) Run(results []stage.ExecutorResult, confAny any) ( + []stage.ParserResult, bool, error, +) { + conf, err := stage.DecodeConf[Conf](confAny) + if err != nil { + return nil, true, err + } + + var res []stage.ParserResult + forceQuit := false + + for _, result := range results { + totalScore := 0 + var commentBuilder strings.Builder + + for i, tier := range conf.Tiers { + conditionsMet := true + var conditionDesc []string + + if tier.TimeLessThan > 0 { + if result.Time < tier.TimeLessThan { + conditionDesc = append( + conditionDesc, + fmt.Sprintf( + "Time < `%d ms`", + tier.TimeLessThan/1e6, + ), + ) + } else { + conditionsMet = false + } + } + + if tier.MemoryLessThan > 0 { + if result.Memory < tier.MemoryLessThan { + conditionDesc = append( + conditionDesc, + fmt.Sprintf( + "Memory < `%.2f MiB`", + float64(tier.MemoryLessThan)/(1024*1024), + ), + ) + } else { + conditionsMet = false + } + } + + if conditionsMet { + totalScore += tier.Score + fmt.Fprintf( + &commentBuilder, + "Tier %d: +%d (meets %s)\n", + i, + tier.Score, + strings.Join(conditionDesc, " and "), + ) + } else { + var required []string + if tier.TimeLessThan > 0 { + required = append( + required, + fmt.Sprintf( + "Time < `%d ms`", + tier.TimeLessThan/1e6, + ), + ) + } + if tier.MemoryLessThan > 0 { + required = append( + required, + fmt.Sprintf( + "Memory < `%.2f MiB`", + float64(tier.MemoryLessThan)/(1024*1024), + ), + ) + } + fmt.Fprintf( + &commentBuilder, + "Tier %d: +0 (requires %s)\n", + i+1, + strings.Join(required, " and "), + ) + } + } + fmt.Fprintf(&commentBuilder, "Final score: %d", totalScore) + res = append(res, stage.ParserResult{ + Score: totalScore, + Comment: commentBuilder.String(), + }) + } + + return res, forceQuit, nil +}