mirror of https://github.com/dirtbags/moth.git
Standardize cat/puzzle command args
This commit is contained in:
parent
8ad42b2dc7
commit
ccd9cea29d
|
@ -7,26 +7,12 @@ import random
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
parser = argparse.ArgumentParser("Generate a puzzle")
|
random.seed(os.getenv("SEED", ""))
|
||||||
parser.add_argument("--file", dest="file", help="File to provide, instead of puzzle")
|
|
||||||
parser.add_argument("--answer", dest="answer", help="Answer to check, instead of providing puzzle")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
seed = hash(os.getenv("SEED"))
|
|
||||||
random.seed(seed)
|
|
||||||
|
|
||||||
words = ["apple", "pear", "peach", "tangerine", "orange", "potato", "carrot", "pea"]
|
words = ["apple", "pear", "peach", "tangerine", "orange", "potato", "carrot", "pea"]
|
||||||
answer = ' '.join(random.sample(words, 4))
|
answer = ' '.join(random.sample(words, 4))
|
||||||
|
|
||||||
if args.file:
|
def puzzle():
|
||||||
f = open(args.file, "rb")
|
|
||||||
shutil.copyfileobj(f, sys.stdout.buffer)
|
|
||||||
elif args.answer:
|
|
||||||
if args.answer == answer:
|
|
||||||
print("correct")
|
|
||||||
else:
|
|
||||||
print("incorrect")
|
|
||||||
else:
|
|
||||||
number = random.randint(20, 500)
|
number = random.randint(20, 500)
|
||||||
obj = {
|
obj = {
|
||||||
"Pre": {
|
"Pre": {
|
||||||
|
@ -53,3 +39,22 @@ else:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
json.dump(obj, sys.stdout)
|
json.dump(obj, sys.stdout)
|
||||||
|
|
||||||
|
def open_file(filename):
|
||||||
|
f = open(filename, "rb")
|
||||||
|
shutil.copyfileobj(f, sys.stdout.buffer)
|
||||||
|
|
||||||
|
def check_answer(check):
|
||||||
|
if answer == check:
|
||||||
|
print("correct")
|
||||||
|
else:
|
||||||
|
print("incorrect")
|
||||||
|
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
puzzle()
|
||||||
|
elif sys.argv[1] == "file":
|
||||||
|
open_file(sys.argv[2])
|
||||||
|
elif sys.argv[1] == "answer":
|
||||||
|
check_answer(sys.argv[2])
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Unknown command: %s" % sys.argv[1])
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -120,13 +121,19 @@ type FsCommandCategory struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inventory returns a list of point values for this category.
|
func (c FsCommandCategory) run(command string, args ...string) ([]byte, error) {
|
||||||
func (c FsCommandCategory) Inventory() ([]int, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, c.command, "inventory")
|
cmdargs := append([]string{command}, args...)
|
||||||
stdout, err := cmd.Output()
|
cmd := exec.CommandContext(ctx, "./"+path.Base(c.command), cmdargs...)
|
||||||
|
cmd.Dir = path.Dir(c.command)
|
||||||
|
return cmd.Output()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inventory returns a list of point values for this category.
|
||||||
|
func (c FsCommandCategory) Inventory() ([]int, error) {
|
||||||
|
stdout, err := c.run("inventory")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -143,11 +150,7 @@ func (c FsCommandCategory) Inventory() ([]int, error) {
|
||||||
func (c FsCommandCategory) Puzzle(points int) (Puzzle, error) {
|
func (c FsCommandCategory) Puzzle(points int) (Puzzle, error) {
|
||||||
var p Puzzle
|
var p Puzzle
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
stdout, err := c.run("puzzle", strconv.Itoa(points))
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, c.command, "puzzle", strconv.Itoa(points))
|
|
||||||
stdout, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return p, err
|
return p, err
|
||||||
}
|
}
|
||||||
|
@ -163,21 +166,13 @@ func (c FsCommandCategory) Puzzle(points int) (Puzzle, error) {
|
||||||
|
|
||||||
// Open returns an io.ReadCloser for the given filename.
|
// Open returns an io.ReadCloser for the given filename.
|
||||||
func (c FsCommandCategory) Open(points int, filename string) (ReadSeekCloser, error) {
|
func (c FsCommandCategory) Open(points int, filename string) (ReadSeekCloser, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
stdout, err := c.run("file", strconv.Itoa(points), filename)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, c.command, "file", strconv.Itoa(points), filename)
|
|
||||||
stdout, err := cmd.Output()
|
|
||||||
return nopCloser{bytes.NewReader(stdout)}, err
|
return nopCloser{bytes.NewReader(stdout)}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Answer checks whether an answer is correct.
|
// Answer checks whether an answer is correct.
|
||||||
func (c FsCommandCategory) Answer(points int, answer string) bool {
|
func (c FsCommandCategory) Answer(points int, answer string) bool {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
stdout, err := c.run("answer", strconv.Itoa(points), answer)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, c.command, "answer", strconv.Itoa(points), answer)
|
|
||||||
stdout, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR: Answering %d points: %s", points, err)
|
log.Printf("ERROR: Answering %d points: %s", points, err)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -3,6 +3,7 @@ package transpile
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,9 @@ func FsInventory(fs afero.Fs) (Inventory, error) {
|
||||||
|
|
||||||
inv := make(Inventory)
|
inv := make(Inventory)
|
||||||
for _, ent := range dirEnts {
|
for _, ent := range dirEnts {
|
||||||
|
if strings.HasPrefix(ent.Name(), ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if ent.IsDir() {
|
if ent.IsDir() {
|
||||||
name := ent.Name()
|
name := ent.Name()
|
||||||
c := NewFsCategory(fs, name)
|
c := NewFsCategory(fs, name)
|
||||||
|
|
|
@ -338,18 +338,19 @@ type FsCommandPuzzle struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp FsCommandPuzzle) run(args ...string) ([]byte, error) {
|
func (fp FsCommandPuzzle) run(command string, args ...string) ([]byte, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), fp.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), fp.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, "./"+path.Base(fp.command), args...)
|
cmdargs := append([]string{command}, args...)
|
||||||
|
cmd := exec.CommandContext(ctx, "./"+path.Base(fp.command), cmdargs...)
|
||||||
cmd.Dir = path.Dir(fp.command)
|
cmd.Dir = path.Dir(fp.command)
|
||||||
return cmd.Output()
|
return cmd.Output()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Puzzle returns a Puzzle struct for the current puzzle.
|
// Puzzle returns a Puzzle struct for the current puzzle.
|
||||||
func (fp FsCommandPuzzle) Puzzle() (Puzzle, error) {
|
func (fp FsCommandPuzzle) Puzzle() (Puzzle, error) {
|
||||||
stdout, err := fp.run()
|
stdout, err := fp.run("puzzle")
|
||||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
return Puzzle{}, errors.New(string(exiterr.Stderr))
|
return Puzzle{}, errors.New(string(exiterr.Stderr))
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -379,7 +380,7 @@ func (c nopCloser) Close() error {
|
||||||
// Open returns a newly-opened file.
|
// Open returns a newly-opened file.
|
||||||
// BUG(neale): FsCommandPuzzle.Open() reads everything into memory, and will suck for large files.
|
// BUG(neale): FsCommandPuzzle.Open() reads everything into memory, and will suck for large files.
|
||||||
func (fp FsCommandPuzzle) Open(filename string) (ReadSeekCloser, error) {
|
func (fp FsCommandPuzzle) Open(filename string) (ReadSeekCloser, error) {
|
||||||
stdout, err := fp.run("--file", filename)
|
stdout, err := fp.run("file", filename)
|
||||||
buf := nopCloser{bytes.NewReader(stdout)}
|
buf := nopCloser{bytes.NewReader(stdout)}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buf, err
|
return buf, err
|
||||||
|
@ -390,7 +391,7 @@ func (fp FsCommandPuzzle) Open(filename string) (ReadSeekCloser, error) {
|
||||||
|
|
||||||
// Answer checks whether the given answer is correct.
|
// Answer checks whether the given answer is correct.
|
||||||
func (fp FsCommandPuzzle) Answer(answer string) bool {
|
func (fp FsCommandPuzzle) Answer(answer string) bool {
|
||||||
stdout, err := fp.run("--answer", answer)
|
stdout, err := fp.run("answer", answer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR: checking answer: %s", err)
|
log.Printf("ERROR: checking answer: %s", err)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -21,8 +21,8 @@ case $1:$2:$3 in
|
||||||
}
|
}
|
||||||
EOT
|
EOT
|
||||||
;;
|
;;
|
||||||
puzzle:*:)
|
puzzle:*)
|
||||||
fail "No such puzzle"
|
fail "No such puzzle: $2"
|
||||||
;;
|
;;
|
||||||
file:1:moo.txt)
|
file:1:moo.txt)
|
||||||
echo "Moo."
|
echo "Moo."
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
case $1 in
|
fail () {
|
||||||
"")
|
echo "ERROR: $*" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1:$2 in
|
||||||
|
puzzle:)
|
||||||
cat <<'EOT'
|
cat <<'EOT'
|
||||||
{
|
{
|
||||||
"Answers": ["answer"],
|
"Answers": ["answer"],
|
||||||
|
@ -12,34 +17,23 @@ case $1 in
|
||||||
}
|
}
|
||||||
EOT
|
EOT
|
||||||
;;
|
;;
|
||||||
-file|--file)
|
file:moo.txt)
|
||||||
case $2 in
|
|
||||||
moo.txt)
|
|
||||||
echo "Moo."
|
echo "Moo."
|
||||||
;;
|
;;
|
||||||
*)
|
file:*)
|
||||||
echo "ERROR: no such file: $1" 1>&2
|
fail "no such file: $1"
|
||||||
exit 1
|
|
||||||
;;
|
;;
|
||||||
esac
|
answer:moo)
|
||||||
;;
|
|
||||||
-answer|--answer)
|
|
||||||
case $2 in
|
|
||||||
moo)
|
|
||||||
echo "correct"
|
echo "correct"
|
||||||
;;
|
;;
|
||||||
error)
|
answer:error)
|
||||||
echo "error" 1>&2
|
fail "you requested an error"
|
||||||
exit 1
|
|
||||||
;;
|
;;
|
||||||
*)
|
answer:*)
|
||||||
echo "incorrect"
|
echo "incorrect"
|
||||||
;;
|
;;
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "ERROR: don't know what to do with $1" 1>&2
|
fail "What is $1"
|
||||||
exit 1
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
@ -88,11 +88,6 @@ function submit(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
let data = new FormData(e.target)
|
let data = new FormData(e.target)
|
||||||
|
|
||||||
// Kludge for patterned answers
|
|
||||||
let xAnswer = data.get("xAnswer")
|
|
||||||
if (xAnswer) {
|
|
||||||
data.set("answer", xAnswer)
|
|
||||||
}
|
|
||||||
window.data = data
|
window.data = data
|
||||||
fetch("answer", {
|
fetch("answer", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -193,7 +188,6 @@ function answerCheck(e) {
|
||||||
|
|
||||||
checkAnswer(answer)
|
checkAnswer(answer)
|
||||||
.then (correct => {
|
.then (correct => {
|
||||||
document.querySelector("[name=xAnswer").value = correct || answer
|
|
||||||
if (correct) {
|
if (correct) {
|
||||||
ok.textContent = "⭕"
|
ok.textContent = "⭕"
|
||||||
ok.title = "Possibly correct"
|
ok.title = "Possibly correct"
|
||||||
|
|
Loading…
Reference in New Issue