From 1fdba38ad31ffa0f3a7fccea8c94eaf774bfcc49 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Tue, 18 Sep 2018 03:32:24 +0000 Subject: [PATCH] Basic rendering of open puzzles --- src/award.go | 4 +-- src/handlers.go | 75 ++++++++++++++++++++++++++++++---------------- src/instance.go | 8 ++--- src/maintenance.go | 4 +-- src/static.go | 71 +++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 127 insertions(+), 35 deletions(-) diff --git a/src/award.go b/src/award.go index acea0cd..759393b 100644 --- a/src/award.go +++ b/src/award.go @@ -64,9 +64,9 @@ func (a *Award) Same(o *Award) bool { case a.TeamId != o.TeamId: return false case a.Category != o.Category: - return false + return false case a.Points != o.Points: return false } return true -} \ No newline at end of file +} diff --git a/src/handlers.go b/src/handlers.go index b522c33..198c27a 100644 --- a/src/handlers.go +++ b/src/handlers.go @@ -23,32 +23,18 @@ func respond(w http.ResponseWriter, req *http.Request, status Status, short stri } } -// anchoredSearch looks for needle in r, -// skipping the first skip space-delimited words -func anchoredSearch(r io.Reader, needle string, skip int) bool { +// hasLine returns true if line appears in r. +// The entire line must match. +func hasLine(r io.Reader, line string) bool { scanner := bufio.NewScanner(r) for scanner.Scan() { - line := scanner.Text() - parts := strings.SplitN(line, " ", skip+1) - if (len(parts) > skip) && (parts[skip] == needle) { + if scanner.Text() == line { return true } } - return false } -// anchoredSearchFile performs an anchoredSearch on a given filename -func anchoredSearchFile(filename string, needle string, skip int) bool { - r, err := os.Open(filename) - if err != nil { - return false - } - defer r.Close() - - return anchoredSearch(r, needle, skip) -} - func (ctx Instance) registerHandler(w http.ResponseWriter, req *http.Request) { teamname := req.FormValue("name") teamid := req.FormValue("id") @@ -69,7 +55,17 @@ func (ctx Instance) registerHandler(w http.ResponseWriter, req *http.Request) { return } - if !anchoredSearchFile(ctx.StatePath("teamids.txt"), teamid, 0) { + teamids, err := os.Open(ctx.StatePath("teamids.txt")) + if err != nil { + respond( + w, req, Fail, + "Cannot read valid team IDs", + "An error was encountered trying to read valid teams IDs: %v", err, + ) + return + } + defer teamids.Close() + if !hasLine(teamids, teamid) { respond( w, req, Fail, "Invalid Team ID", @@ -137,7 +133,7 @@ func (ctx Instance) tokenHandler(w http.ResponseWriter, req *http.Request) { defer f.Close() // Make sure the token is in the list - if !anchoredSearch(f, token, 0) { + if !hasLine(f, token) { respond( w, req, Fail, "Unrecognized token", @@ -190,7 +186,7 @@ func (ctx Instance) answerHandler(w http.ResponseWriter, req *http.Request) { // Look for the answer needle := fmt.Sprintf("%d %s", points, answer) - if !anchoredSearch(haystack, needle, 0) { + if !hasLine(haystack, needle) { respond( w, req, Fail, "Wrong answer", @@ -215,23 +211,46 @@ func (ctx Instance) answerHandler(w http.ResponseWriter, req *http.Request) { } type PuzzleMap struct { - Points int `json:"points"` - Path string `json:"path"` + Points int + Path string +} + +func (pm *PuzzleMap) MarshalJSON() ([]byte, error) { + if pm == nil { + return []byte("null"), nil + } + + jPath, err := json.Marshal(pm.Path) + if err != nil { + return nil, err + } + + ret := fmt.Sprintf("[%d,%s]", pm.Points, string(jPath)) + return []byte(ret), nil } func (ctx Instance) puzzlesHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) + maxByCategory := map[string]int{} + for _, a := range ctx.PointsLog() { + if a.Points > maxByCategory[a.Category] { + maxByCategory[a.Category] = a.Points + } + } + res := map[string][]PuzzleMap{} for catName, mb := range ctx.Categories { mf, err := mb.Open("map.txt") if err != nil { log.Print(err) + continue } defer mf.Close() pm := make([]PuzzleMap, 0, 30) + completed := true scanner := bufio.NewScanner(mf) for scanner.Scan() { line := scanner.Text() @@ -249,11 +268,17 @@ func (ctx Instance) puzzlesHandler(w http.ResponseWriter, req *http.Request) { } pm = append(pm, PuzzleMap{pointval, dir}) - log.Print(pm) + + if pointval > maxByCategory[catName] { + completed = false + break + } + } + if completed { + pm = append(pm, PuzzleMap{0, ""}) } res[catName] = pm - log.Print(res) } jres, _ := json.Marshal(res) w.Write(jres) diff --git a/src/instance.go b/src/instance.go index f35a14b..0ecdbcf 100644 --- a/src/instance.go +++ b/src/instance.go @@ -121,10 +121,10 @@ func (ctx *Instance) PointsLog() []*Award { // The maintenance task makes sure we never have duplicate points in the log. func (ctx *Instance) AwardPoints(teamid, category string, points int) error { a := Award{ - When: time.Now(), - TeamId: teamid, + When: time.Now(), + TeamId: teamid, Category: category, - Points: points, + Points: points, } for _, e := range ctx.PointsLog() { @@ -132,7 +132,7 @@ func (ctx *Instance) AwardPoints(teamid, category string, points int) error { return fmt.Errorf("Points already awarded to this team in this category") } } - + fn := fmt.Sprintf("%s-%s-%d", teamid, category, points) tmpfn := ctx.StatePath("points.tmp", fn) newfn := ctx.StatePath("points.new", fn) diff --git a/src/maintenance.go b/src/maintenance.go index 75d839b..518c92d 100644 --- a/src/maintenance.go +++ b/src/maintenance.go @@ -90,7 +90,7 @@ func (ctx *Instance) CollectPoints() { log.Printf("Can't parse award file %s: %s", filename, err) continue } - + duplicate := false for _, e := range ctx.PointsLog() { if award.Same(e) { @@ -98,7 +98,7 @@ func (ctx *Instance) CollectPoints() { break } } - + if duplicate { log.Printf("Skipping duplicate points: %s", award.String()) } else { diff --git a/src/static.go b/src/static.go index ef209d3..ee788ca 100644 --- a/src/static.go +++ b/src/static.go @@ -119,6 +119,13 @@ input { } li { margin: 0.5em 0em; +} +nav { + border: solid black 2px; +} +nav li { + display: inline; + margin: 2em; } `, ) @@ -158,8 +165,68 @@ func staticScoreboard(w http.ResponseWriter) { func staticPuzzles(w http.ResponseWriter) { ShowHtml( w, Success, - "Puzzles", - "XXX: This would be the puzzles overview", + "Open Puzzles", + ` +
+ + `, ) }