diff --git a/client/syzojclient/client.go b/client/syzojclient/client.go index 97c2506..6167768 100644 --- a/client/syzojclient/client.go +++ b/client/syzojclient/client.go @@ -37,7 +37,8 @@ type Client struct { socket engineio.Conn tasks chan client.Task - progress chan *types.ProgressProgressed + progress chan *result + result chan *result finish chan *types.JudgeResult request chan struct{} ack chan ack @@ -60,7 +61,8 @@ func NewClient(url, token string) (*Client, chan error, error) { token: token, socket: socket, tasks: make(chan client.Task, buffSize), - progress: make(chan *types.ProgressProgressed, buffSize), + progress: make(chan *result, buffSize), + result: make(chan *result, buffSize), finish: make(chan *types.JudgeResult, buffSize), request: make(chan struct{}, 1), ack: make(chan ack, 1), @@ -153,6 +155,27 @@ func (c *Client) writeLoop() (err error) { return err } + sendProgress := func(event string, p interface{}) error { + var d []byte + if err := codec.NewEncoderBytes(&d, &codec.MsgpackHandle{}).Encode(p); err != nil { + return err + } + + // binary encoding + buff := &parser.Buffer{ + Data: d, + } + + if err := c.encoder.Encode(parser.Header{ + Type: parser.Event, + Namespace: namespace, + NeedAck: true, + }, []interface{}{event, c.token, buff}); err != nil { + return err + } + return nil + } + for { select { case <-c.Done: @@ -167,6 +190,16 @@ func (c *Client) writeLoop() (err error) { return err } + case p := <-c.progress: + if err := sendProgress("reportProgress", p); err != nil { + return err + } + + case r := <-c.result: + if err := sendProgress("reportResult", r); err != nil { + return err + } + case a := <-c.ack: if err := c.encoder.Encode(parser.Header{ Type: parser.Ack, @@ -219,6 +252,7 @@ func newTask(c *Client, msg *judgeTask, ackID uint64) client.Task { client: c, task: task, ackID: ackID, + taskID: msg.Content.TaskID, parsed: make(chan *types.ProblemConfig), compiled: make(chan *types.ProgressCompiled), diff --git a/client/syzojclient/progress.go b/client/syzojclient/progress.go index a143139..a29ecde 100644 --- a/client/syzojclient/progress.go +++ b/client/syzojclient/progress.go @@ -49,41 +49,41 @@ type result struct { } type progress struct { - Status taskStatus `json:"status"` - Message string `json:"message"` + Status taskStatus `json:"status,omitempty"` + Message string `json:"message,omitempty"` - Error *errType `json:"error"` - SystemMessage *string `json:"systemMessage"` - Compile *compileResult `json:"compile"` - Judge *judgeResult `json:"judge"` + Error *errType `json:"error,omitempty"` + SystemMessage *string `json:"systemMessage,omitempty"` + Compile *compileResult `json:"compile,omitempty"` + Judge *judgeResult `json:"judge,omitempty"` } type judgeResult struct { - Subtasks []subtaskResult `json:"subtasks"` + Subtasks []subtaskResult `json:"subtasks,omitempty"` } type subtaskResult struct { - Score *float64 `json:"score"` - Cases []caseResult `json:"cases"` + Score float64 `json:"score,omitempty"` + Cases []caseResult `json:"cases,omitempty"` } type caseResult struct { Status taskStatus `json:"status"` - Result *testcaseDetails `json:"result"` - Error *string `json:"errorMessage"` + Result *testcaseDetails `json:"result,omitempty"` + Error string `json:"errorMessage,omitempty"` } type testcaseDetails struct { Status testCaseResultType `json:"type"` Time uint64 `json:"time"` // ms Memory uint64 `json:"memory"` // kb - Input *fileContent `json:"input"` - Output *fileContent `json:"output"` - ScoringRate float64 `json:"scoringRate"` - UserOutput *string `json:"userOutput"` - UserError *string `json:"userError"` - SPJMessage *string `json:"spjError"` - SystemMessage *string `json:"systemMessage"` + Input *fileContent `json:"input,omitempty"` + Output *fileContent `json:"output,omitempty"` + ScoringRate float64 `json:"scoringRate,omitempty"` + UserOutput *string `json:"userOutput,omitempty"` + UserError *string `json:"userError,omitempty"` + SPJMessage *string `json:"spjError,omitempty"` + SystemMessage *string `json:"systemMessage,omitempty"` } type fileContent struct { diff --git a/client/syzojclient/task.go b/client/syzojclient/task.go index cd86a40..33b9201 100644 --- a/client/syzojclient/task.go +++ b/client/syzojclient/task.go @@ -14,6 +14,7 @@ type Task struct { client *Client task *types.JudgeTask ackID uint64 + taskID string parsed chan *types.ProblemConfig compiled chan *types.ProgressCompiled @@ -47,24 +48,148 @@ func (t *Task) Finished(r *types.JudgeResult) { } func (t *Task) loop() { + var ( + jr judgeResult + cr *compileResult + ) loop: for { select { case pConf := <-t.parsed: log.Println(pConf) + initResult(pConf, &jr) + rt := &result{ + TaskID: t.taskID, + Type: progressStarted, + } + t.client.progress <- rt case compiled := <-t.compiled: log.Println(compiled) + cr = &compileResult{ + Status: convertStatus(compiled.Status), + Message: compiled.Message, + } + rt := &result{ + TaskID: t.taskID, + Type: progressCompiled, + Progress: progress{ + Status: convertStatus(compiled.Status), + Message: compiled.Message, + }, + } + t.client.progress <- rt + t.client.result <- rt case progressed := <-t.progressed: log.Println(progressed) + updateResult(progressed, &jr) + rt := &result{ + TaskID: t.taskID, + Type: progressProgress, + Progress: progress{ + Compile: cr, + Judge: &jr, + }, + } + t.client.progress <- rt case finished := <-t.finished: log.Println(finished) + rt := &result{ + TaskID: t.taskID, + Type: progressFinished, + Progress: progress{ + Compile: cr, + Judge: &jr, + }, + } + t.client.progress <- rt + t.client.result <- rt + t.client.ack <- ack{id: t.ackID} t.client.request <- struct{}{} break loop } } } + +func initResult(p *types.ProblemConfig, jr *judgeResult) { + jr.Subtasks = make([]subtaskResult, len(p.Subtasks)) + for i := range jr.Subtasks { + initSubtaskResult(&p.Subtasks[i], &jr.Subtasks[i]) + } +} + +func initSubtaskResult(p *types.SubTask, sr *subtaskResult) { + sr.Cases = make([]caseResult, len(p.Cases)) +} + +func convertStatus(s types.ProgressStatus) taskStatus { + switch s { + case types.ProgressSucceeded: + return statusDone + default: + return statusFailed + } +} + +func convertResultTypes(s types.Status) testCaseResultType { + switch s { + case types.StatusAccepted: + return resultAccepted + case types.StatusWrongAnswer: + return resultWrongAnswer + case types.StatusPartiallyCorrect: + return resultPartiallyCorrect + case types.StatusMemoryLimitExceeded: + return resultMemoryLimitExceeded + case types.StatusTimeLimitExceeded: + return resultTimeLimitExceeded + case types.StatusOutputLimitExceeded: + return resultOutputLimitExceeded + case types.StatusFileError: + return resultFileError + case types.StatusRuntimeError: + return resultRuntimeError + case types.StatusJudgementFailed: + return resultJudgementFailed + case types.StatusInvalidInteraction: + return resultInvalidInteraction + default: + return resultRuntimeError + } +} + +func updateResult(p *types.ProgressProgressed, jr *judgeResult) { + st := &jr.Subtasks[p.SubTaskIndex] + st.Score += 100 * p.ScoreRate / float64(len(jr.Subtasks[p.SubTaskIndex].Cases)) + + cr := &st.Cases[p.TestCaseIndex] + cr.Status = convertStatus(p.Status) + cr.Error = p.Error + cr.Result = &testcaseDetails{ + Status: convertResultTypes(p.ExecStatus), + Time: p.Time, + Memory: p.Memory, + Input: getFileContent("input", p.Input), + Output: getFileContent("output", p.Answer), + ScoringRate: p.ScoreRate, + UserOutput: getStringP(p.UserOutput), + UserError: getStringP(p.UserError), + SPJMessage: getStringP(p.SPJOutput), + } +} + +func getFileContent(name string, b []byte) *fileContent { + return &fileContent{ + Name: name, + Content: string(b), + } +} + +func getStringP(b []byte) *string { + s := string(b) + return &s +}