2019-08-17 16:00:15 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-09-03 20:04:43 -06:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2019-08-18 21:59:06 -06:00
|
|
|
"log"
|
|
|
|
"strconv"
|
|
|
|
|
2020-08-28 17:41:17 -06:00
|
|
|
"github.com/spf13/afero"
|
|
|
|
)
|
2019-08-18 21:59:06 -06:00
|
|
|
|
2020-09-03 20:04:43 -06:00
|
|
|
type NopReadCloser struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n NopReadCloser) Read(b []byte) (int, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
func (n NopReadCloser) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFsCategory returns a Category based on which files are present.
|
|
|
|
// If 'mkcategory' is present and executable, an FsCommandCategory is returned.
|
|
|
|
// Otherwise, FsCategory is returned.
|
|
|
|
func NewFsCategory(fs afero.Fs) Category {
|
|
|
|
if info, err := fs.Stat("mkcategory"); (err == nil) && (info.Mode()&0100 != 0) {
|
|
|
|
return FsCommandCategory{fs: fs}
|
|
|
|
} else {
|
|
|
|
return FsCategory{fs: fs}
|
2019-08-18 21:59:06 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 20:04:43 -06:00
|
|
|
type FsCategory struct {
|
|
|
|
fs afero.Fs
|
2020-08-28 17:41:17 -06:00
|
|
|
}
|
2019-08-18 21:59:06 -06:00
|
|
|
|
2020-09-03 20:04:43 -06:00
|
|
|
// Category returns a list of puzzle values.
|
|
|
|
func (c FsCategory) Inventory() ([]int, error) {
|
|
|
|
puzzleEntries, err := afero.ReadDir(c.fs, ".")
|
2019-08-18 21:59:06 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-08-25 18:50:38 -06:00
|
|
|
|
2020-08-28 17:41:17 -06:00
|
|
|
puzzles := make([]int, 0, len(puzzleEntries))
|
|
|
|
for _, ent := range puzzleEntries {
|
|
|
|
if !ent.IsDir() {
|
|
|
|
continue
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|
2020-08-28 17:41:17 -06:00
|
|
|
if points, err := strconv.Atoi(ent.Name()); err != nil {
|
|
|
|
log.Println("Skipping non-numeric directory", ent.Name())
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
puzzles = append(puzzles, points)
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|
|
|
|
}
|
2020-08-28 17:41:17 -06:00
|
|
|
return puzzles, nil
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|
|
|
|
|
2020-09-03 20:04:43 -06:00
|
|
|
func (c FsCategory) Puzzle(points int) (Puzzle, error) {
|
|
|
|
return NewFsPuzzle(c.fs, points).Puzzle()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c FsCategory) Open(points int, filename string) (io.ReadCloser, error) {
|
|
|
|
return NewFsPuzzle(c.fs, points).Open(filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c FsCategory) Answer(points int, answer string) bool {
|
|
|
|
// BUG(neale): FsCategory.Answer should probably always return false, to prevent you from running uncompiled puzzles with participants.
|
|
|
|
p, err := c.Puzzle(points)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for _, a := range p.Answers {
|
|
|
|
if a == answer {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
type FsCommandCategory struct {
|
|
|
|
fs afero.Fs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c FsCommandCategory) Inventory() ([]int, error) {
|
|
|
|
return nil, fmt.Errorf("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c FsCommandCategory) Puzzle(points int) (Puzzle, error) {
|
|
|
|
return Puzzle{}, fmt.Errorf("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c FsCommandCategory) Open(points int, filename string) (io.ReadCloser, error) {
|
|
|
|
return NopReadCloser{}, fmt.Errorf("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c FsCommandCategory) Answer(points int, answer string) bool {
|
|
|
|
return false
|
2019-08-18 21:59:06 -06:00
|
|
|
}
|