Basic rendering of open puzzles

This commit is contained in:
Neale Pickett 2018-09-18 03:32:24 +00:00
parent 5070c70d25
commit 5e4af17d57
5 changed files with 127 additions and 35 deletions

View File

@ -64,7 +64,7 @@ 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
}

View File

@ -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)

View File

@ -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() {

View File

@ -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",
`
<div id="puzzles"></div>
<script>
function render(obj) {
let element = document.getElementById("puzzles");
let cats = [];
for (let cat in obj) {
cats.push(cat);
console.log(cat);
}
cats.sort();
for (let cat of cats) {
let puzzles = obj[cat];
let pdiv = document.createElement('div');
pdiv.className = 'category';
let h = document.createElement('h2');
pdiv.appendChild(h);
h.textContent = cat;
let l = document.createElement('ul');
pdiv.appendChild(l);
for (var puzzle of puzzles) {
var points = puzzle[0];
var id = puzzle[1];
var i = document.createElement('li');
l.appendChild(i);
if (points === 0) {
i.textContent = "‡";
} else {
var a = document.createElement('a');
i.appendChild(a);
a.textContent = points;
a.href = cat + "/" + id + "/index.html";
}
}
element.appendChild(pdiv);
}
}
function init() {
fetch("puzzles.json")
.then(function(resp) {
return resp.json();
}).then(function(obj) {
render(obj);
}).catch(function(err) {
console.log("Error", err);
});
}
window.addEventListener("load", init);
</script>
`,
)
}