Reset for 2023
This commit is contained in:
parent
4f2a3c34af
commit
2769a7f9de
32 changed files with 0 additions and 2128 deletions
|
@ -1,128 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as patches
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
OUTPUT_FILE = sys.argv[1]
|
||||
YEAR = sys.argv[2]
|
||||
|
||||
COLOURS = {"Golang": "#00ADD8", "Python": "#3572A5", "Nim": "#ffc200"}
|
||||
|
||||
MAX_Y_VALUE = 1
|
||||
|
||||
challenge_dir_regex = re.compile("""(?m)^(\d{2})-([a-zA-Z]+)$""")
|
||||
|
||||
directories = []
|
||||
path = os.path.join("challenges", YEAR)
|
||||
for filename in os.listdir(path):
|
||||
if os.path.isdir(os.path.join(path, filename)) and challenge_dir_regex.match(
|
||||
filename
|
||||
):
|
||||
directories.append(filename)
|
||||
|
||||
files = [os.path.join(x, "benchmark.json") for x in directories]
|
||||
|
||||
benchmark_data = {
|
||||
"Python": {},
|
||||
"Golang": {},
|
||||
"Nim": {},
|
||||
} # adding dicts here sets the order of points being plotted
|
||||
|
||||
for filename in files:
|
||||
fpath = os.path.join(path, filename)
|
||||
try:
|
||||
f = open(fpath)
|
||||
except FileNotFoundError:
|
||||
print(f"Warning: missing file {fpath}")
|
||||
continue
|
||||
|
||||
data = json.load(f)
|
||||
f.close()
|
||||
|
||||
for language in data["implementations"]:
|
||||
x = benchmark_data.get(language, {})
|
||||
x[str(data["day"]) + ".1"] = data["implementations"][language]["part.1.avg"]
|
||||
x[str(data["day"]) + ".2"] = data["implementations"][language]["part.2.avg"]
|
||||
benchmark_data[language] = x
|
||||
|
||||
all_days = set()
|
||||
|
||||
for lang in benchmark_data:
|
||||
for key in benchmark_data[lang]:
|
||||
day = int(key.split(".", 1)[0])
|
||||
all_days.add(day)
|
||||
|
||||
figure = plt.figure(figsize=(25 / 2, 5))
|
||||
axp1 = figure.add_subplot(1, 2, 1)
|
||||
axp2 = figure.add_subplot(1, 2, 2, sharey=axp1)
|
||||
|
||||
axp1.axhline(y=15, color="#fc8080", linestyle="--")
|
||||
axp2.axhline(y=15, color="#fc8080", linestyle="--")
|
||||
|
||||
for i, language in enumerate(benchmark_data):
|
||||
|
||||
data = benchmark_data[language]
|
||||
part_one_times = []
|
||||
part_two_times = []
|
||||
days = []
|
||||
|
||||
for key in data:
|
||||
|
||||
day = int(key.split(".", 1)[0])
|
||||
if day not in days:
|
||||
days.append(day)
|
||||
|
||||
if key.endswith(".1"):
|
||||
part_one_times.append(data[key])
|
||||
if key.endswith(".2"):
|
||||
part_two_times.append(data[key])
|
||||
|
||||
colour = COLOURS.get(language)
|
||||
|
||||
p1 = axp1.scatter(days, part_one_times, color=colour)
|
||||
p2 = axp2.scatter(days, part_two_times, color=colour)
|
||||
|
||||
for i, day in enumerate(days):
|
||||
if i + 1 >= len(days):
|
||||
continue
|
||||
if days[i + 1] == day + 1:
|
||||
axp1.plot(
|
||||
(day, days[i + 1]),
|
||||
(part_one_times[i], part_one_times[i + 1]),
|
||||
"-",
|
||||
color=colour,
|
||||
)
|
||||
axp2.plot(
|
||||
(day, days[i + 1]),
|
||||
(part_two_times[i], part_two_times[i + 1]),
|
||||
"-",
|
||||
color=colour,
|
||||
)
|
||||
|
||||
figure.suptitle(f"Average {YEAR} challenge running time")
|
||||
axp1.set_title("Part one")
|
||||
axp2.set_title("Part two")
|
||||
|
||||
|
||||
def do_auxillary_parts(axis):
|
||||
plt.sca(axis)
|
||||
plt.xticks(list(all_days), [str(y) for y in all_days])
|
||||
plt.ylabel("Running time (seconds)")
|
||||
plt.yscale("log")
|
||||
plt.xlabel("Day")
|
||||
plt.legend(
|
||||
handles=[patches.Patch(color=COLOURS[label], label=label) for label in COLOURS]
|
||||
)
|
||||
# plt.ylim([0, MAX_Y_VALUE])
|
||||
# plt.legend(legends)
|
||||
|
||||
|
||||
do_auxillary_parts(axp1)
|
||||
do_auxillary_parts(axp2)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(OUTPUT_FILE)
|
57
get-input.py
57
get-input.py
|
@ -1,57 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import datetime
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
default_config_file_name = os.path.join(script_dir, "input-loader.json")
|
||||
|
||||
today = datetime.date.today()
|
||||
|
||||
parser = argparse.ArgumentParser(description="Process some integers.")
|
||||
parser.add_argument(
|
||||
"--config-file",
|
||||
dest="config_file",
|
||||
default=default_config_file_name,
|
||||
help="config file name",
|
||||
)
|
||||
parser.add_argument("--day", default=today.day, help="day to get input for")
|
||||
parser.add_argument("--year", default=today.year, help="year to get input for")
|
||||
parser.add_argument(
|
||||
"--stdout",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="send the input to stdout instead of a file",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.config_file) as f:
|
||||
config_data = json.load(f)
|
||||
|
||||
r = requests.get(
|
||||
f"https://adventofcode.com/{args.year}/day/{args.day}/input",
|
||||
cookies={"session": config_data["session"]},
|
||||
headers={"User-Agent": config_data["userAgent"]},
|
||||
)
|
||||
|
||||
if args.stdout:
|
||||
sys.stdout.write(r.text + "\n")
|
||||
sys.stderr.write("OK\n")
|
||||
else:
|
||||
year_dir = os.path.join("challenges", str(args.year))
|
||||
directory_name = None
|
||||
for entry in os.listdir(year_dir):
|
||||
if entry.startswith(f"{args.day}-"):
|
||||
directory_name = entry
|
||||
break
|
||||
assert directory_name is not None, "challenge directory must already exist"
|
||||
path = os.path.join("challenges", str(args.year), directory_name, "input.txt")
|
||||
with open(path, "w") as f:
|
||||
f.write(r.text + "\n")
|
||||
|
||||
sys.stderr.write(f"Written to {path}\n")
|
27
go.mod
27
go.mod
|
@ -1,27 +0,0 @@
|
|||
module github.com/codemicro/adventOfCode
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.2
|
||||
github.com/alexflint/go-arg v1.4.2
|
||||
github.com/deckarep/golang-set v1.8.0
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/schollz/progressbar/v3 v3.8.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alexflint/go-scalar v1.0.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
)
|
78
go.sum
78
go.sum
|
@ -1,78 +0,0 @@
|
|||
github.com/AlecAivazis/survey/v2 v2.3.2 h1:TqTB+aDDCLYhf9/bD2TwSO8u8jDSmMUd2SUVO4gCnU8=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.2/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0=
|
||||
github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM=
|
||||
github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70=
|
||||
github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8=
|
||||
github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
@ -1,62 +0,0 @@
|
|||
package aocgo
|
||||
|
||||
import "errors"
|
||||
|
||||
type BaseChallenge struct{}
|
||||
|
||||
func (b BaseChallenge) One(instr string) (interface{}, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (b BaseChallenge) Two(instr string) (interface{}, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (b BaseChallenge) Vis(instr string, outdir string) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func IntPermutations(arr []int) [][]int {
|
||||
var helper func([]int, int)
|
||||
res := [][]int{}
|
||||
|
||||
helper = func(arr []int, n int) {
|
||||
if n == 1 {
|
||||
tmp := make([]int, len(arr))
|
||||
copy(tmp, arr)
|
||||
res = append(res, tmp)
|
||||
} else {
|
||||
for i := 0; i < n; i++ {
|
||||
helper(arr, n-1)
|
||||
if n%2 == 1 {
|
||||
tmp := arr[i]
|
||||
arr[i] = arr[n-1]
|
||||
arr[n-1] = tmp
|
||||
} else {
|
||||
tmp := arr[0]
|
||||
arr[0] = arr[n-1]
|
||||
arr[n-1] = tmp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
helper(arr, len(arr))
|
||||
return res
|
||||
}
|
||||
|
||||
func StringPermutations(x string) []string {
|
||||
var asInts []int
|
||||
for _, char := range x {
|
||||
asInts = append(asInts, int(char))
|
||||
}
|
||||
ip := IntPermutations(asInts)
|
||||
var o []string
|
||||
for _, x := range ip {
|
||||
var b string
|
||||
for _, y := range x {
|
||||
b += string(rune(y))
|
||||
}
|
||||
o = append(o, b)
|
||||
}
|
||||
return o
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package aocgo
|
||||
|
||||
type Set []interface{}
|
||||
|
||||
func NewSet() *Set {
|
||||
return new(Set)
|
||||
}
|
||||
|
||||
func (s *Set) Contains(x interface{}) bool {
|
||||
for _, item := range *s {
|
||||
if item == x {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Set) Add(x interface{}) {
|
||||
if !s.Contains(x) {
|
||||
*s = append(*s, x)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set) Union(t *Set) {
|
||||
for _, item := range *t {
|
||||
s.Add(item)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set) ShallowCopy() *Set {
|
||||
ns := NewSet()
|
||||
*ns = append(*ns, *s...)
|
||||
return ns
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
from __future__ import annotations
|
||||
from typing import *
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
class BaseChallenge:
|
||||
@staticmethod
|
||||
def one(instr: str) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def two(instr: str) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def vis(instr: str, outputDir: str) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
|
||||
|
||||
def foldl(p: Callable[[U, T], U], i: Iterable[T], start: U) -> U:
|
||||
res = start
|
||||
for item in i:
|
||||
res = p(res, item)
|
||||
return res
|
||||
|
||||
|
||||
def foldr(p: Callable[[U, T], U], i: Iterable[T], start: U) -> U:
|
||||
return foldl(p, reversed(i), start)
|
||||
|
||||
|
||||
def min_max(x: Iterable[int]) -> Tuple[int, int]:
|
||||
mini, maxi = None, 0
|
||||
|
||||
for item in x:
|
||||
if item > maxi:
|
||||
maxi = item
|
||||
if mini is None or item < mini:
|
||||
mini = item
|
||||
|
||||
if mini is None:
|
||||
raise ValueError("empty set")
|
||||
|
||||
return mini, maxi
|
||||
|
||||
|
||||
class Vector:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
def __init__(self, *args):
|
||||
if len(args) == 1 and Vector._is_vector_tuple(args[0]):
|
||||
x, y = args[0]
|
||||
elif len(args) != 2:
|
||||
raise ValueError("expected integer tuple or pair of integers")
|
||||
else:
|
||||
x, y = args
|
||||
|
||||
self.x = int(x)
|
||||
self.y = int(y)
|
||||
|
||||
@staticmethod
|
||||
def _is_vector_tuple(o: Any) -> bool:
|
||||
return (
|
||||
type(o) == tuple and len(o) == 2 and type(o[0]) == int and type(o[1]) == int
|
||||
)
|
||||
|
||||
def manhattan_distance(self, o: Vector) -> int:
|
||||
return abs(self.x - o.x) + abs(self.y - o.y)
|
||||
|
||||
@property
|
||||
def tuple(self) -> Tuple[int, int]:
|
||||
return self.x, self.y
|
||||
|
||||
def __add__(self, o: Any) -> Vector:
|
||||
if Vector._is_vector_tuple(o):
|
||||
return Vector(self.x + o[0], self.y + o[1])
|
||||
elif type(o) == Vector:
|
||||
return Vector(self.x + o.x, self.y + o.y)
|
||||
else:
|
||||
raise ValueError(f"cannot add Vector and {type(o)}")
|
||||
|
||||
def __sub__(self, o: Any) -> Vector:
|
||||
if Vector._is_vector_tuple(o):
|
||||
return Vector(self.x - o[0], self.y - o[1])
|
||||
elif type(o) == Vector:
|
||||
return Vector(self.x - o.x, self.y - o.y)
|
||||
else:
|
||||
raise ValueError(f"cannot subtract Vector and {type(o)}")
|
||||
|
||||
def __eq__(self, o: Any) -> bool:
|
||||
if Vector._is_vector_tuple(o):
|
||||
return self.x == o[0] and self.y == o[1]
|
||||
elif type(o) == Vector:
|
||||
return self.x == o.x and self.y == o.y
|
||||
else:
|
||||
raise ValueError(f"cannot equate Vector and {type(o)}")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
# return f"Vector(x={self.x}, y={self.y})"
|
||||
return f"({self.x}, {self.y})"
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.x, self.y))
|
||||
|
||||
|
||||
class Consumer:
|
||||
x: Sequence[T]
|
||||
i: int
|
||||
|
||||
def __init__(self, x: Sequence[T]):
|
||||
self.x = x
|
||||
self.i = 0
|
||||
|
||||
def take(self) -> T:
|
||||
self.i += 1
|
||||
return self.x[self.i - 1]
|
||||
|
||||
def undo(self):
|
||||
self.i -= 1
|
||||
|
||||
|
||||
class RepeatingConsumer(Consumer):
|
||||
def take(self) -> T:
|
||||
val = super().take()
|
||||
self.i = self.i % len(self.x)
|
||||
return val
|
||||
|
||||
def undo(self):
|
||||
super().undo()
|
||||
if self.i < 0:
|
||||
self.i += len(self.x)
|
|
@ -1,11 +0,0 @@
|
|||
import os.path
|
||||
|
||||
|
||||
class SaveManager:
|
||||
def __init__(self, d):
|
||||
self.dir = d
|
||||
self.current_n = 0
|
||||
|
||||
def save(self, im):
|
||||
im.save(os.path.join(self.dir, f"frame_{str(self.current_n).zfill(4)}.png"))
|
||||
self.current_n += 1
|
|
@ -1,86 +0,0 @@
|
|||
# AoC runtime
|
||||
|
||||
In brief: the runtime indexes available challenge implementations in the current working directory, loads inputs for a given challenge from disk and runs those inputs against a variety of available implementations for that challenge.
|
||||
|
||||
## Challenge discovery
|
||||
|
||||
The `./challenges` directory is indexed. Every subdirectory of `./challenges` is considered a "year". Every subdirectory within a given year that matches the regular expression `^(\d{2})-([a-zA-Z]+)$` (in practise, if it looks something like `05-somethingOrOther`) is considered a "challenge".
|
||||
|
||||
## Challenge data
|
||||
|
||||
Within every challenge, there should be a `info.json` file. This file should contain the filename of the main challenge input relative to the challenge directory and any test cases that can be run against a solution. An example is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"inputFile": "input.txt",
|
||||
"testCases": {
|
||||
"one": [
|
||||
{
|
||||
"input": "8A004A801A8002F478",
|
||||
"expected": "16"
|
||||
},
|
||||
{
|
||||
"input": "620080001611562C8802118E34",
|
||||
"expected": "12"
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"input": "C200B40A82",
|
||||
"expected": "3"
|
||||
},
|
||||
{
|
||||
"input": "04005AC33890",
|
||||
"expected": "54"
|
||||
},
|
||||
{
|
||||
"input": "880086C3E88112",
|
||||
"expected": "7"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Challenge implementations
|
||||
|
||||
Any subdirectory in a challenge that has a name equal to one of the map keys defined in `Available` in [`./runners/runners.go`](./runners/runners.go) is considered a "challenge implementation".
|
||||
|
||||
## Running a challenge implementation
|
||||
|
||||
Once a challenge and implemetation has been selected, it is run by instantiating a runner inside the challenge directory. The type of runner is dependent on the challenge implementation selected.
|
||||
|
||||
Each runner will wrap the challenge code in some language specific wrapper code, then run that wrapper.
|
||||
|
||||
## Communicating with running challenges
|
||||
|
||||
Running challenge implemnentations recieve their inputs in JSON format, via `stdin`. A sample input might look like this:
|
||||
|
||||
```json
|
||||
{"task_id": "test.1.0", "part": 1, "input": "8A004A801A8002F478"}
|
||||
```
|
||||
|
||||
The running challenge implementation then processes the input, and returns a result via `stdout`, which might look soemthing like this:
|
||||
|
||||
```json
|
||||
{"task_id": "test.1.0", "ok": true, "output": "16", "duration": 9.131431579589844e-05}
|
||||
```
|
||||
|
||||
The format of both the input and output JSON is defined in [`./runners/comm.go`](./runners/comm.go) as the `Task` and `Result` structs.
|
||||
|
||||
The prefix of a task ID can be used within the wrapper to determine the type of task being run.
|
||||
|
||||
* A `test` prefix indicates a test is being run.
|
||||
* A `main` prefix indicates that the main input is being used.
|
||||
* A `vis` prefix indicates that a visualisation is being run.
|
||||
* A `benchmark` prefix indicates that a given task is part of a benchmark.
|
||||
|
||||
**A running challenge implementation must return results in the same order they were returned in.**
|
||||
|
||||
## Debugging output
|
||||
|
||||
If anything is sent by a running challenge implementation via `stdout` that is not valid JSON, it will be passed through to the `stdout` of the runtime program.
|
||||
|
||||
## Stopping a running challenge implementation
|
||||
|
||||
There is no way for the runtime to communicate to a running implementation that it needs to shut down. Instead, the runtime forcibly kills the running implementation.
|
|
@ -1,182 +0,0 @@
|
|||
package benchmark
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/codemicro/adventOfCode/runtime/challenge"
|
||||
"github.com/codemicro/adventOfCode/runtime/runners"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
func makeBenchmarkID(part runners.Part, number int) string {
|
||||
if number == -1 {
|
||||
return fmt.Sprintf("benchmark.part.%d", part)
|
||||
}
|
||||
return fmt.Sprintf("benchmark.part.%d.%d", part, number)
|
||||
}
|
||||
|
||||
func meanFloatSlice(arr []float64) float64 {
|
||||
var sum float64
|
||||
for _, v := range arr {
|
||||
sum += v
|
||||
}
|
||||
return sum / float64(len(arr))
|
||||
}
|
||||
|
||||
func minFloatSlice(arr []float64) float64 {
|
||||
min := arr[0]
|
||||
for _, v := range arr {
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
func maxFloatSlice(arr []float64) float64 {
|
||||
max := arr[0]
|
||||
for _, v := range arr {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
func Run(selectedChallenge *challenge.Challenge, input string, numberRuns int) error {
|
||||
|
||||
implementations, err := selectedChallenge.GetImplementations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var valueSets []*values
|
||||
|
||||
for _, implementation := range implementations {
|
||||
v, err := benchmarkImplementation(implementation, selectedChallenge.Dir, input, numberRuns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valueSets = append(valueSets, v)
|
||||
}
|
||||
|
||||
// make file
|
||||
jdata := make(map[string]interface{})
|
||||
jdata["day"] = selectedChallenge.Number
|
||||
jdata["dir"] = selectedChallenge.Dir
|
||||
jdata["numRuns"] = numberRuns
|
||||
jdata["implementations"] = make(map[string]interface{})
|
||||
|
||||
for _, vs := range valueSets {
|
||||
x := make(map[string]interface{})
|
||||
for _, v := range vs.values {
|
||||
x[v.key] = v.value
|
||||
}
|
||||
(jdata["implementations"].(map[string]interface{}))[vs.implementation] = x
|
||||
}
|
||||
|
||||
fpath := filepath.Join(selectedChallenge.Dir, "benchmark.json")
|
||||
|
||||
fmt.Println("Writing results to", fpath)
|
||||
|
||||
jBytes, err := json.MarshalIndent(jdata, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(
|
||||
fpath,
|
||||
jBytes,
|
||||
0644,
|
||||
)
|
||||
}
|
||||
|
||||
type values struct {
|
||||
implementation string
|
||||
values []kv
|
||||
}
|
||||
|
||||
type kv struct {
|
||||
key string
|
||||
value float64
|
||||
}
|
||||
|
||||
func benchmarkImplementation(implementation string, dir string, inputString string, numberRuns int) (*values, error) {
|
||||
|
||||
var (
|
||||
tasks []*runners.Task
|
||||
results []*runners.Result
|
||||
)
|
||||
|
||||
runner := runners.Available[implementation](dir)
|
||||
for i := 0; i < numberRuns; i++ {
|
||||
|
||||
tasks = append(tasks, &runners.Task{
|
||||
TaskID: makeBenchmarkID(runners.PartOne, i),
|
||||
Part: runners.PartOne,
|
||||
Input: inputString,
|
||||
}, &runners.Task{
|
||||
TaskID: makeBenchmarkID(runners.PartTwo, i),
|
||||
Part: runners.PartTwo,
|
||||
Input: inputString,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
pb := progressbar.NewOptions(
|
||||
numberRuns * 2, // two parts means 2x the number of runs
|
||||
progressbar.OptionSetDescription(
|
||||
fmt.Sprintf("Running %s benchmarks", runners.RunnerNames[implementation]),
|
||||
),
|
||||
)
|
||||
|
||||
if err := runner.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = runner.Stop()
|
||||
_ = runner.Cleanup()
|
||||
}()
|
||||
|
||||
for _, task := range tasks {
|
||||
res, err := runner.Run(task)
|
||||
if err != nil {
|
||||
_ = pb.Close()
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, res)
|
||||
_ = pb.Add(1)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
var (
|
||||
p1, p2 []float64
|
||||
p1id = makeBenchmarkID(runners.PartOne, -1)
|
||||
p2id = makeBenchmarkID(runners.PartTwo, -1)
|
||||
)
|
||||
|
||||
for _, result := range results {
|
||||
if strings.HasPrefix(result.TaskID, p1id) {
|
||||
p1 = append(p1, result.Duration)
|
||||
} else if strings.HasPrefix(result.TaskID, p2id) {
|
||||
p2 = append(p2, result.Duration)
|
||||
}
|
||||
}
|
||||
|
||||
return &values{
|
||||
implementation: runners.RunnerNames[implementation],
|
||||
values: []kv{
|
||||
{"part.1.avg", meanFloatSlice(p1)},
|
||||
{"part.1.min", minFloatSlice(p1)},
|
||||
{"part.1.max", maxFloatSlice(p1)},
|
||||
{"part.2.avg", meanFloatSlice(p2)},
|
||||
{"part.2.min", minFloatSlice(p2)},
|
||||
{"part.2.max", maxFloatSlice(p2)},
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package challenge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/codemicro/adventOfCode/runtime/util"
|
||||
)
|
||||
|
||||
type Challenge struct {
|
||||
Number int
|
||||
Name string
|
||||
Dir string
|
||||
}
|
||||
|
||||
func (c *Challenge) String() string {
|
||||
return fmt.Sprintf("%d - %s", c.Number, c.Name)
|
||||
}
|
||||
|
||||
var challengeDirRegexp = regexp.MustCompile(`(?m)^(\d{2})-([a-zA-Z]+)$`)
|
||||
|
||||
func ListingFromDir(sourceDir string) ([]*Challenge, error) {
|
||||
|
||||
dirEntries, err := os.ReadDir(sourceDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var o []*Challenge
|
||||
for _, entry := range dirEntries {
|
||||
|
||||
if entry.IsDir() && challengeDirRegexp.MatchString(entry.Name()) {
|
||||
dir := entry.Name()
|
||||
|
||||
x := strings.Split(dir, "-")
|
||||
dayInt, _ := strconv.Atoi(x[0]) // error ignored because regex should have ensured this is ok
|
||||
dayTitle := util.CamelToTitle(x[1])
|
||||
o = append(o, &Challenge{
|
||||
Number: dayInt,
|
||||
Name: dayTitle,
|
||||
Dir: filepath.Join(sourceDir, dir),
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package challenge
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
InputFile string `json:"inputFile"`
|
||||
TestCases struct {
|
||||
One []*TestCase `json:"one"`
|
||||
Two []*TestCase `json:"two"`
|
||||
} `json:"testCases"`
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
Input string `json:"input"`
|
||||
Expected string `json:"expected"`
|
||||
}
|
||||
|
||||
func LoadChallengeInfo(fname string) (*Info, error) {
|
||||
|
||||
fcont, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := new(Info)
|
||||
err = json.Unmarshal(fcont, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package challenge
|
||||
|
||||
import (
|
||||
"github.com/codemicro/adventOfCode/runtime/runners"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *Challenge) GetImplementations() ([]string, error) {
|
||||
dirEntries, err := os.ReadDir(c.Dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var o []string
|
||||
for _, de := range dirEntries {
|
||||
if !de.IsDir() {
|
||||
continue
|
||||
}
|
||||
if _, ok := runners.Available[strings.ToLower(de.Name())]; ok {
|
||||
o = append(o, de.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
147
runtime/main.go
147
runtime/main.go
|
@ -1,147 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/codemicro/adventOfCode/runtime/benchmark"
|
||||
au "github.com/logrusorgru/aurora"
|
||||
|
||||
"github.com/alexflint/go-arg"
|
||||
"github.com/codemicro/adventOfCode/runtime/challenge"
|
||||
"github.com/codemicro/adventOfCode/runtime/runners"
|
||||
)
|
||||
|
||||
const (
|
||||
challengeDir = "challenges"
|
||||
challengeInfoFile = "info.json"
|
||||
)
|
||||
|
||||
var args struct {
|
||||
Year string `arg:"-y,--year" help:"AoC year to use"`
|
||||
ChallengeDay *int `arg:"-d,--day" help:"challenge day number to run"`
|
||||
Implementation string `arg:"-i,--implementation" help:"implementation to use"`
|
||||
Benchmark bool `arg:"-b,--benchmark" help:"benchmark a day's implementations'"`
|
||||
BenchmarkN int `arg:"-n,--benchmark-n" help:"Number of iterations to run for benchmarking" default:"1000"`
|
||||
TestOnly bool `arg:"-t,--test-only" help:"Only run test inputs"`
|
||||
NoTest bool `arg:"-x,--no-test" help:"Do not run test inputs"`
|
||||
Visualise bool `arg:"-g,--visualise" help:"Run visualisation generation"`
|
||||
}
|
||||
|
||||
func run() error {
|
||||
|
||||
arg.MustParse(&args)
|
||||
|
||||
// List and select year
|
||||
selectedYear, err := selectYear(challengeDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// List and select challenges
|
||||
selectedChallenge, err := selectChallenge(selectedYear)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load info.json file
|
||||
challengeInfo, err := challenge.LoadChallengeInfo(filepath.Join(selectedChallenge.Dir, challengeInfoFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load challenge input
|
||||
challengeInput, err := ioutil.ReadFile(filepath.Join(selectedChallenge.Dir, challengeInfo.InputFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
challengeInputString := string(challengeInput)
|
||||
|
||||
if args.Benchmark {
|
||||
return benchmark.Run(selectedChallenge, challengeInputString, args.BenchmarkN)
|
||||
}
|
||||
|
||||
// List and select implementations
|
||||
selectedImplementation, err := selectImplementation(selectedChallenge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(
|
||||
au.Bold(
|
||||
fmt.Sprintf(
|
||||
"%s-%d %s (%s)\n\n",
|
||||
strings.TrimPrefix(selectedYear, "challenges/"),
|
||||
selectedChallenge.Number,
|
||||
selectedChallenge.Name,
|
||||
runners.RunnerNames[selectedImplementation],
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
runner := runners.Available[selectedImplementation](selectedChallenge.Dir)
|
||||
if err := runner.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = runner.Stop()
|
||||
_ = runner.Cleanup()
|
||||
}()
|
||||
|
||||
if args.Visualise {
|
||||
id := "vis"
|
||||
r, err := runner.Run(&runners.Task{
|
||||
TaskID: id,
|
||||
Part: runners.Visualise,
|
||||
Input: challengeInputString,
|
||||
OutputDir: ".", // directory the runner is run in, which is the challenge directory
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(au.Bold("Visualisation: "))
|
||||
|
||||
var status string
|
||||
var followUpText string
|
||||
if !r.Ok {
|
||||
status = incompleteLabel
|
||||
followUpText = "saying \"" + r.Output + "\""
|
||||
} else {
|
||||
status = passLabel
|
||||
}
|
||||
|
||||
if followUpText == "" {
|
||||
followUpText = fmt.Sprintf("in %.4f seconds", r.Duration)
|
||||
}
|
||||
|
||||
fmt.Print(status)
|
||||
fmt.Println(au.Gray(10, " "+followUpText))
|
||||
|
||||
} else {
|
||||
|
||||
fmt.Print("Running...\n\n")
|
||||
|
||||
if !args.NoTest {
|
||||
if err := runTests(runner, challengeInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !args.TestOnly {
|
||||
if err := runMainTasks(runner, challengeInputString); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package runners
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
au "github.com/logrusorgru/aurora"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
TaskID string `json:"task_id"`
|
||||
Part Part `json:"part"`
|
||||
Input string `json:"input"`
|
||||
OutputDir string `json:"output_dir,omitempty"`
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
TaskID string `json:"task_id"`
|
||||
Ok bool `json:"ok"`
|
||||
Output string `json:"output"`
|
||||
Duration float64 `json:"duration"`
|
||||
}
|
||||
|
||||
type customWriter struct {
|
||||
pending []byte
|
||||
entries [][]byte
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func (c *customWriter) Write(b []byte) (int, error) {
|
||||
var n int
|
||||
|
||||
c.mux.Lock()
|
||||
for _, x := range b {
|
||||
if x == '\n' {
|
||||
c.entries = append(c.entries, c.pending)
|
||||
c.pending = nil
|
||||
} else {
|
||||
c.pending = append(c.pending, x)
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
c.mux.Unlock()
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *customWriter) GetEntry() ([]byte, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
if len(c.entries) == 0 {
|
||||
return nil, errors.New("no entries")
|
||||
}
|
||||
var x []byte
|
||||
x, c.entries = c.entries[0], c.entries[1:]
|
||||
return x, nil
|
||||
}
|
||||
|
||||
func setupBuffers(cmd *exec.Cmd) (io.WriteCloser, error) {
|
||||
stdoutWriter := &customWriter{}
|
||||
cmd.Stdout = stdoutWriter
|
||||
cmd.Stderr = new(bytes.Buffer)
|
||||
return cmd.StdinPipe()
|
||||
}
|
||||
|
||||
func checkWait(cmd *exec.Cmd) ([]byte, error) {
|
||||
c := cmd.Stdout.(*customWriter)
|
||||
for {
|
||||
e, err := c.GetEntry()
|
||||
if err == nil {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
if cmd.ProcessState != nil {
|
||||
// this is only populated after program exit - we have an issue
|
||||
return nil, fmt.Errorf("run failed with exit code %d: %s", cmd.ProcessState.ExitCode(), cmd.Stderr.(*bytes.Buffer).String())
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
}
|
||||
|
||||
func readJSONFromCommand(res interface{}, cmd *exec.Cmd) error {
|
||||
|
||||
for {
|
||||
inp, err := checkWait(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(inp, res)
|
||||
if err != nil {
|
||||
// echo anything that won't parse to stdout (this lets us add debug print statements)
|
||||
fmt.Printf("[%s] %v\n", au.BrightRed("DBG"), strings.TrimSpace(string(inp)))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
package runners
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
golangInstallation = "go"
|
||||
golangWrapperFilename = "runtime-wrapper.go"
|
||||
golangWrapperExecutableFilename = "runtime-wrapper"
|
||||
)
|
||||
|
||||
type golangRunner struct {
|
||||
dir string
|
||||
cmd *exec.Cmd
|
||||
wrapperFilepath string
|
||||
executableFilepath string
|
||||
stdin io.WriteCloser
|
||||
}
|
||||
|
||||
func newGolangRunner(dir string) Runner {
|
||||
return &golangRunner{
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed interface/go.go
|
||||
var golangInterface []byte
|
||||
|
||||
func (g *golangRunner) Start() error {
|
||||
g.wrapperFilepath = filepath.Join(g.dir, golangWrapperFilename)
|
||||
g.executableFilepath = filepath.Join(g.dir, golangWrapperExecutableFilename)
|
||||
|
||||
// determine package import path
|
||||
buildPath := fmt.Sprintf("github.com/codemicro/adventOfCode/challenges/%s/%s", filepath.Base(filepath.Dir(g.dir)), filepath.Base(g.dir))
|
||||
importPath := buildPath + "/go"
|
||||
|
||||
// generate code
|
||||
var wrapperContent []byte
|
||||
{
|
||||
tpl := template.Must(template.New("").Parse(string(golangInterface)))
|
||||
b := new(bytes.Buffer)
|
||||
err := tpl.Execute(b, struct {
|
||||
ImportPath string
|
||||
}{importPath})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wrapperContent = b.Bytes()
|
||||
}
|
||||
|
||||
// save interaction code
|
||||
if err := ioutil.WriteFile(g.wrapperFilepath, wrapperContent, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// compile executable
|
||||
stderrBuffer := new(bytes.Buffer)
|
||||
|
||||
cmd := exec.Command(golangInstallation, "build", "-tags", "runtime", "-o", g.executableFilepath, buildPath)
|
||||
cmd.Stderr = stderrBuffer
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("compilation failed: %s: %s", err, stderrBuffer.String())
|
||||
}
|
||||
|
||||
if !cmd.ProcessState.Success() {
|
||||
return errors.New("compilation failed, hence cannot continue")
|
||||
}
|
||||
|
||||
// now we run!
|
||||
absExecPath, err := filepath.Abs(g.executableFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// run executable
|
||||
g.cmd = exec.Command(absExecPath)
|
||||
cmd.Dir = g.dir
|
||||
|
||||
if stdin, err := setupBuffers(g.cmd); err != nil {
|
||||
return err
|
||||
} else {
|
||||
g.stdin = stdin
|
||||
}
|
||||
|
||||
return g.cmd.Start()
|
||||
}
|
||||
|
||||
func (g *golangRunner) Stop() error {
|
||||
if g.cmd == nil || g.cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
return g.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
func (g *golangRunner) Cleanup() error {
|
||||
if g.wrapperFilepath != "" {
|
||||
_ = os.Remove(g.wrapperFilepath)
|
||||
}
|
||||
if g.executableFilepath != "" {
|
||||
_ = os.Remove(g.executableFilepath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *golangRunner) Run(task *Task) (*Result, error) {
|
||||
taskJSON, err := json.Marshal(task)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _ = g.stdin.Write(append(taskJSON, '\n'))
|
||||
|
||||
res := new(Result)
|
||||
if err := readJSONFromCommand(res, g.cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
//+build runtime
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/codemicro/adventOfCode/runtime/runners"
|
||||
"os"
|
||||
"time"
|
||||
chcode "{{ .ImportPath }}"
|
||||
)
|
||||
|
||||
func sendResult(taskID string, ok bool, output string, duration float64) {
|
||||
x := runners.Result{
|
||||
TaskID: taskID,
|
||||
Ok: ok,
|
||||
Output: output,
|
||||
Duration: duration,
|
||||
}
|
||||
dat, err := json.Marshal(&x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(dat))
|
||||
}
|
||||
|
||||
func run() error {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
task := new(runners.Task)
|
||||
taskBytes, err := reader.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(taskBytes, task); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var run func() (interface{}, error)
|
||||
|
||||
switch task.Part {
|
||||
case runners.PartOne:
|
||||
run = func() (interface{}, error) {
|
||||
return (chcode.Challenge{}).One(task.Input)
|
||||
}
|
||||
case runners.PartTwo:
|
||||
run = func() (interface{}, error) {
|
||||
return (chcode.Challenge{}).Two(task.Input)
|
||||
}
|
||||
case runners.Visualise:
|
||||
run = func() (interface{}, error) {
|
||||
return "", (chcode.Challenge{}).Vis(task.Input, task.OutputDir)
|
||||
}
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
res, err := run()
|
||||
endTIme := time.Now()
|
||||
|
||||
runningTime := endTIme.Sub(startTime).Seconds()
|
||||
|
||||
if err != nil {
|
||||
sendResult(task.TaskID, false, err.Error(), runningTime)
|
||||
} else {
|
||||
sendResult(task.TaskID, true, fmt.Sprintf("%v", res), runningTime)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import std/strutils
|
||||
import std/options
|
||||
import std/json
|
||||
import std/monotimes
|
||||
import std/times
|
||||
|
||||
from nim/challenge as solutions import nil
|
||||
|
||||
proc sendResult(taskID: string, ok: bool, output: string, duration: float64) =
|
||||
let jobj = %* {
|
||||
"task_id": taskID,
|
||||
"ok": ok,
|
||||
"output": output,
|
||||
"duration": duration,
|
||||
}
|
||||
echo $jobj
|
||||
|
||||
type
|
||||
Task = ref object
|
||||
task_id: string
|
||||
part: int
|
||||
input: string
|
||||
output_dir: Option[string]
|
||||
|
||||
while true:
|
||||
|
||||
let
|
||||
taskString = readLine(stdin)
|
||||
task = to(parseJson(taskString), Task)
|
||||
|
||||
var runProc: proc(): string
|
||||
|
||||
case task.part
|
||||
of 1:
|
||||
runProc = proc(): string = $(solutions.partOne(task.input))
|
||||
of 2:
|
||||
runProc = proc(): string = $(solutions.partTwo(task.input))
|
||||
else:
|
||||
sendResult(task.task_id, false, "unknown task part", 0.0)
|
||||
|
||||
var
|
||||
result: string
|
||||
error: string
|
||||
|
||||
let startTime = getMonoTime()
|
||||
try:
|
||||
result = runProc()
|
||||
except:
|
||||
error = getCurrentExceptionMsg()
|
||||
let endTime = getMonoTime()
|
||||
|
||||
let runningTime = endTime - startTime
|
||||
let runningTimeSeconds = float(inNanoseconds(runningTime)) / float(1000000000)
|
||||
|
||||
if error != "":
|
||||
sendResult(task.task_id, false, error, runningTimeSeconds)
|
||||
else:
|
||||
sendResult(task.task_id, true, result, runningTimeSeconds)
|
|
@ -1,58 +0,0 @@
|
|||
from py import Challenge
|
||||
|
||||
import time
|
||||
import json
|
||||
|
||||
# TASKS_STR = input()
|
||||
# TASKS = json.loads(TASKS_STR)
|
||||
|
||||
|
||||
def send_result(task_id, ok, output, duration):
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"task_id": task_id,
|
||||
"ok": ok,
|
||||
"output": str(output) if output is not None else "",
|
||||
"duration": float(duration),
|
||||
}
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
|
||||
|
||||
while True:
|
||||
task = json.loads(input())
|
||||
taskPart = task["part"]
|
||||
task_id = task["task_id"]
|
||||
|
||||
run = None
|
||||
|
||||
if taskPart == 1:
|
||||
run = lambda: Challenge.one(task["input"])
|
||||
elif taskPart == 2:
|
||||
run = lambda: Challenge.two(task["input"])
|
||||
elif taskPart == 3:
|
||||
run = lambda: Challenge.vis(task["input"], task["output_dir"])
|
||||
else:
|
||||
send_result(task_id, False, "unknown task part", 0)
|
||||
continue
|
||||
|
||||
start_time = time.time()
|
||||
res = None
|
||||
err = None
|
||||
try:
|
||||
res = run()
|
||||
except Exception as e:
|
||||
err = f"{type(e)}: {e}"
|
||||
import traceback
|
||||
|
||||
err = f"{type(e)}: {e}\n{''.join(traceback.format_tb(e.__traceback__))}"
|
||||
end_time = time.time()
|
||||
|
||||
running_time = end_time - start_time
|
||||
|
||||
if err is not None:
|
||||
send_result(task_id, False, err, running_time)
|
||||
else:
|
||||
send_result(task_id, True, res, running_time)
|
|
@ -1,109 +0,0 @@
|
|||
package runners
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
nimInstallation = "nim"
|
||||
nimWrapperFilename = "runtimeWrapper.nim"
|
||||
nimWrapperExecutableFilename = "runtimeWrapper"
|
||||
)
|
||||
|
||||
type nimRunner struct {
|
||||
dir string
|
||||
cmd *exec.Cmd
|
||||
wrapperFilepath string
|
||||
executableFilepath string
|
||||
stdin io.WriteCloser
|
||||
}
|
||||
|
||||
func newNimRunner(dir string) Runner {
|
||||
return &nimRunner{
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed interface/nim.nim
|
||||
var nimInterface []byte
|
||||
|
||||
func (n *nimRunner) Start() error {
|
||||
n.wrapperFilepath = filepath.Join(n.dir, nimWrapperFilename)
|
||||
n.executableFilepath = filepath.Join(n.dir, nimWrapperExecutableFilename)
|
||||
|
||||
// save interaction code
|
||||
err := ioutil.WriteFile(n.wrapperFilepath, nimInterface, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// compile
|
||||
stderrBuffer := new(bytes.Buffer)
|
||||
cmd := exec.Command(nimInstallation, "compile", "-o:"+n.executableFilepath, "-d:release", n.wrapperFilepath)
|
||||
cmd.Stderr = stderrBuffer
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("compilation failed: %s: %s", err, stderrBuffer.String())
|
||||
}
|
||||
|
||||
if !cmd.ProcessState.Success() {
|
||||
return errors.New("compilation failed, hence cannot continue")
|
||||
}
|
||||
|
||||
// now we run!
|
||||
absExecPath, err := filepath.Abs(n.executableFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.cmd = exec.Command(absExecPath)
|
||||
n.cmd.Dir = n.dir
|
||||
|
||||
if stdin, err := setupBuffers(n.cmd); err != nil {
|
||||
return err
|
||||
} else {
|
||||
n.stdin = stdin
|
||||
}
|
||||
|
||||
return n.cmd.Start()
|
||||
}
|
||||
|
||||
func (n *nimRunner) Stop() error {
|
||||
if n.cmd == nil || n.cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
return n.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
func (n *nimRunner) Cleanup() error {
|
||||
if n.wrapperFilepath != "" {
|
||||
_ = os.Remove(n.wrapperFilepath)
|
||||
}
|
||||
if n.executableFilepath != "" {
|
||||
_ = os.Remove(n.executableFilepath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nimRunner) Run(task *Task) (*Result, error) {
|
||||
taskJSON, err := json.Marshal(task)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _ = n.stdin.Write(append(taskJSON, '\n'))
|
||||
|
||||
res := new(Result)
|
||||
if err := readJSONFromCommand(res, n.cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
package runners
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
python3Installation = "python3"
|
||||
pythonWrapperFilename = "runtime-wrapper.py"
|
||||
)
|
||||
|
||||
type pythonRunner struct {
|
||||
dir string
|
||||
cmd *exec.Cmd
|
||||
wrapperFilepath string
|
||||
stdin io.WriteCloser
|
||||
}
|
||||
|
||||
func newPythonRunner(dir string) Runner {
|
||||
return &pythonRunner{
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed interface/python.py
|
||||
var pythonInterface []byte
|
||||
|
||||
func (p *pythonRunner) Start() error {
|
||||
p.wrapperFilepath = filepath.Join(p.dir, pythonWrapperFilename)
|
||||
|
||||
// Save interaction code
|
||||
if err := ioutil.WriteFile(p.wrapperFilepath, pythonInterface, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sort out PYTHONPATH
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
absDir, err := filepath.Abs(p.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pythonPathVar := strings.Join([]string{
|
||||
filepath.Join(cwd, "lib"), // so we can use aocpy
|
||||
filepath.Join(absDir, "py"), // so we can import stuff in the challenge directory
|
||||
}, ":")
|
||||
|
||||
p.cmd = exec.Command(python3Installation, "-B", pythonWrapperFilename) // -B prevents .pyc files from being written
|
||||
p.cmd.Env = append(p.cmd.Env, "PYTHONPATH="+pythonPathVar)
|
||||
p.cmd.Dir = p.dir
|
||||
|
||||
if stdin, err := setupBuffers(p.cmd); err != nil {
|
||||
return err
|
||||
} else {
|
||||
p.stdin = stdin
|
||||
}
|
||||
|
||||
return p.cmd.Start()
|
||||
}
|
||||
|
||||
func (p *pythonRunner) Stop() error {
|
||||
if p.cmd == nil || p.cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
return p.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
func (p *pythonRunner) Cleanup() error {
|
||||
if p.wrapperFilepath == "" {
|
||||
return nil
|
||||
}
|
||||
_ = os.Remove(p.wrapperFilepath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pythonRunner) Run(task *Task) (*Result, error) {
|
||||
taskJSON, err := json.Marshal(task)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _ = p.stdin.Write(append(taskJSON, '\n'))
|
||||
|
||||
res := new(Result)
|
||||
if err := readJSONFromCommand(res, p.cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package runners
|
||||
|
||||
type Part uint8
|
||||
|
||||
const (
|
||||
PartOne Part = iota + 1
|
||||
PartTwo
|
||||
Visualise
|
||||
)
|
||||
|
||||
type Runner interface {
|
||||
Start() error
|
||||
Stop() error
|
||||
Cleanup() error
|
||||
Run(task *Task) (*Result, error)
|
||||
}
|
||||
|
||||
type ResultOrError struct {
|
||||
Result *Result
|
||||
Error error
|
||||
}
|
||||
|
||||
type RunnerCreator func(dir string) Runner
|
||||
|
||||
var Available = map[string]RunnerCreator{
|
||||
"py": newPythonRunner,
|
||||
"go": newGolangRunner,
|
||||
"nim": newNimRunner,
|
||||
}
|
||||
|
||||
var RunnerNames = map[string]string{
|
||||
"py": "Python",
|
||||
"go": "Golang",
|
||||
"nim": "Nim",
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/codemicro/adventOfCode/runtime/challenge"
|
||||
"github.com/codemicro/adventOfCode/runtime/runners"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func userSelect(question string, choices []string) (int, error) {
|
||||
var o string
|
||||
prompt := &survey.Select{
|
||||
Message: question,
|
||||
Options: choices,
|
||||
}
|
||||
//err := survey.AskOne(prompt, &o, survey.WithStdio(os.Stdin, os.Stderr, os.Stderr))
|
||||
err := survey.AskOne(prompt, &o)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for i, x := range choices {
|
||||
if x == o {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func selectYear(dir string) (string, error) {
|
||||
|
||||
var opts []string
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
opts = append(opts, entry.Name())
|
||||
}
|
||||
|
||||
if len(opts) == 0 {
|
||||
return "", errors.New("no years to use")
|
||||
}
|
||||
|
||||
if args.Year != "" {
|
||||
for _, x := range opts {
|
||||
if x == args.Year {
|
||||
return filepath.Join(dir, x), nil
|
||||
}
|
||||
}
|
||||
fmt.Printf("Could not locate year %s\n", args.Year)
|
||||
}
|
||||
|
||||
var selectedYearIndex int
|
||||
|
||||
if x := len(opts); x == 1 {
|
||||
selectedYearIndex = 0
|
||||
} else {
|
||||
selectedYearIndex, err = userSelect("Which year do you want to use?", opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return filepath.Join(dir, opts[selectedYearIndex]), nil
|
||||
}
|
||||
|
||||
func selectChallenge(dir string) (*challenge.Challenge, error) {
|
||||
|
||||
challenges, err := challenge.ListingFromDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(challenges) == 0 {
|
||||
return nil, errors.New("no challenges to run")
|
||||
}
|
||||
|
||||
if args.ChallengeDay != nil {
|
||||
for _, ch := range challenges {
|
||||
if ch.Number == *args.ChallengeDay {
|
||||
return ch, nil
|
||||
}
|
||||
}
|
||||
fmt.Printf("Could not locate day %d\n", *args.ChallengeDay)
|
||||
}
|
||||
|
||||
var selectedChallengeIndex int
|
||||
|
||||
if x := len(challenges); x == 1 {
|
||||
selectedChallengeIndex = 0
|
||||
} else {
|
||||
var opts []string
|
||||
for _, c := range challenges {
|
||||
opts = append(opts, c.String())
|
||||
}
|
||||
|
||||
selectedChallengeIndex, err = userSelect("Which challenge do you want to run?", opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return challenges[selectedChallengeIndex], nil
|
||||
}
|
||||
|
||||
func selectImplementation(ch *challenge.Challenge) (string, error) {
|
||||
|
||||
implementations, err := ch.GetImplementations()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(implementations) == 0 {
|
||||
return "", errors.New("no implementations to use")
|
||||
}
|
||||
|
||||
if args.Implementation != "" {
|
||||
for _, im := range implementations {
|
||||
if strings.EqualFold(im, args.Implementation) {
|
||||
return im, nil
|
||||
}
|
||||
}
|
||||
fmt.Printf("Could not locate implementation %#v\n", args.Implementation)
|
||||
}
|
||||
|
||||
var selectedImplementationIndex int
|
||||
|
||||
if x := len(implementations); x == 1 {
|
||||
selectedImplementationIndex = 0
|
||||
} else {
|
||||
var opts []string
|
||||
for _, i := range implementations {
|
||||
opts = append(opts, runners.RunnerNames[i])
|
||||
}
|
||||
|
||||
selectedImplementationIndex, err = userSelect("Which implementation do you want to use?", opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return implementations[selectedImplementationIndex], nil
|
||||
}
|
132
runtime/tasks.go
132
runtime/tasks.go
|
@ -1,132 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/codemicro/adventOfCode/runtime/challenge"
|
||||
"github.com/codemicro/adventOfCode/runtime/runners"
|
||||
au "github.com/logrusorgru/aurora"
|
||||
)
|
||||
|
||||
var (
|
||||
passLabel = au.BrightGreen("pass").String()
|
||||
failLabel = au.BrightRed("fail").String()
|
||||
incompleteLabel = au.BgBrightRed("did not complete").String()
|
||||
)
|
||||
|
||||
func makeTestID(part runners.Part, n int) string {
|
||||
return fmt.Sprintf("test.%d.%d", part, n)
|
||||
}
|
||||
|
||||
func parseTestID(x string) (runners.Part, int) {
|
||||
y := strings.Split(x, ".")
|
||||
p, _ := strconv.Atoi(y[1])
|
||||
n, _ := strconv.Atoi(y[2])
|
||||
return runners.Part(p), n
|
||||
}
|
||||
|
||||
func makeMainID(part runners.Part) string {
|
||||
return fmt.Sprintf("main.%d", part)
|
||||
}
|
||||
|
||||
func parseMainID(x string) runners.Part {
|
||||
y := strings.Split(x, ".")
|
||||
p, _ := strconv.Atoi(y[1])
|
||||
return runners.Part(p)
|
||||
}
|
||||
|
||||
func runTests(runner runners.Runner, info *challenge.Info) error {
|
||||
for i, testCase := range info.TestCases.One {
|
||||
id := makeTestID(runners.PartOne, i)
|
||||
result, err := runner.Run(&runners.Task{
|
||||
TaskID: id,
|
||||
Part: runners.PartOne,
|
||||
Input: testCase.Input,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handleTestResult(result, testCase)
|
||||
}
|
||||
|
||||
for i, testCase := range info.TestCases.Two {
|
||||
id := makeTestID(runners.PartTwo, i)
|
||||
result, err := runner.Run(&runners.Task{
|
||||
TaskID: id,
|
||||
Part: runners.PartTwo,
|
||||
Input: testCase.Input,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handleTestResult(result, testCase)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleTestResult(r *runners.Result, testCase *challenge.TestCase) {
|
||||
part, n := parseTestID(r.TaskID)
|
||||
|
||||
fmt.Print(au.Bold(fmt.Sprintf("Test %s: ",
|
||||
au.BrightBlue(fmt.Sprintf("%d.%d", part, n)),
|
||||
)))
|
||||
|
||||
passed := r.Output == testCase.Expected
|
||||
|
||||
var status string
|
||||
var followUpText string
|
||||
if !r.Ok {
|
||||
status = incompleteLabel
|
||||
followUpText = "saying \"" + r.Output + "\""
|
||||
} else if passed {
|
||||
status = passLabel
|
||||
} else {
|
||||
status = failLabel
|
||||
}
|
||||
|
||||
if followUpText == "" {
|
||||
followUpText = fmt.Sprintf("in %.4f seconds", r.Duration)
|
||||
}
|
||||
|
||||
fmt.Print(status)
|
||||
fmt.Println(au.Gray(10, " "+followUpText))
|
||||
|
||||
if !passed && r.Ok {
|
||||
fmt.Printf(" └ Expected %s, got %s\n", au.BrightBlue(testCase.Expected), au.BrightBlue(r.Output))
|
||||
}
|
||||
}
|
||||
|
||||
func runMainTasks(runner runners.Runner, input string) error {
|
||||
for part := runners.PartOne; part <= runners.PartTwo; part += 1 {
|
||||
id := makeMainID(part)
|
||||
result, err := runner.Run(&runners.Task{
|
||||
TaskID: id,
|
||||
Part: part,
|
||||
Input: input,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handleMainResult(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleMainResult(r *runners.Result) {
|
||||
part := parseMainID(r.TaskID)
|
||||
|
||||
fmt.Print(au.Bold(fmt.Sprintf("Part %d: ", au.Yellow(part))))
|
||||
|
||||
if !r.Ok {
|
||||
fmt.Print(incompleteLabel)
|
||||
fmt.Println(au.Gray(10, " saying \""+r.Output+"\""))
|
||||
} else {
|
||||
fmt.Print(au.BrightBlue(r.Output))
|
||||
fmt.Println(au.Gray(10, fmt.Sprintf(" in %.4f seconds", r.Duration)))
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func CamelToTitle(x string) string {
|
||||
var o string
|
||||
for i, char := range x {
|
||||
if i == 0 {
|
||||
o += string(unicode.ToUpper(char))
|
||||
} else if unicode.IsUpper(char) {
|
||||
o += " " + string(char)
|
||||
} else {
|
||||
o += string(char)
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"_extensions": ["local_extensions.camel_case", "local_extensions.current_year", "local_extensions.current_day"],
|
||||
|
||||
"year": "{{ 0 | current_year }}",
|
||||
"dayNumber": "{{ 0 | current_day }}",
|
||||
"challengeTitle": null,
|
||||
|
||||
"__formattedTitle": "{{ cookiecutter.challengeTitle | title }}",
|
||||
"__camelTitle": "{{ cookiecutter.challengeTitle | camel_case }}",
|
||||
"__formattedDayNumber": "{{ '%02d' | format(cookiecutter.dayNumber|int) }}"
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import datetime
|
||||
from cookiecutter.utils import simple_filter
|
||||
|
||||
|
||||
@simple_filter
|
||||
def camel_case(v):
|
||||
res = v.split(" ")
|
||||
for i in range(len(res)):
|
||||
f = lambda x: x.upper()
|
||||
if i == 0:
|
||||
f = lambda x: x.lower()
|
||||
|
||||
res[i] = f(res[i][0]) + res[i][1:]
|
||||
|
||||
return "".join(res)
|
||||
|
||||
|
||||
@simple_filter
|
||||
def current_year(_):
|
||||
return datetime.datetime.now().year
|
||||
|
||||
|
||||
@simple_filter
|
||||
def current_day(_):
|
||||
return datetime.datetime.now().day
|
|
@ -1 +0,0 @@
|
|||
# [Day {{ cookiecutter.dayNumber }}: {{ cookiecutter.__formattedTitle }}](https://adventofcode.com/{{ cookiecutter.year }}/day/{{ cookiecutter.dayNumber }})
|
|
@ -1,15 +0,0 @@
|
|||
package challenge
|
||||
|
||||
import "github.com/codemicro/adventOfCode/lib/aocgo"
|
||||
|
||||
type Challenge struct {
|
||||
aocgo.BaseChallenge
|
||||
}
|
||||
|
||||
func (c Challenge) One(instr string) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c Challenge) Two(instr string) (any, error) {
|
||||
return nil, nil
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"inputFile": "input.txt",
|
||||
"testCases": {
|
||||
"one": [
|
||||
{
|
||||
"input": "",
|
||||
"expected": ""
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"input": "",
|
||||
"expected": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
proc partOne*(instr: string): untyped =
|
||||
raise newException(CatchableError, "not implemented")
|
||||
|
||||
proc partTwo*(instr: string): untyped =
|
||||
raise newException(CatchableError, "not implemented")
|
|
@ -1,12 +0,0 @@
|
|||
from typing import *
|
||||
from aocpy import BaseChallenge
|
||||
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def two(instr: str) -> int:
|
||||
raise NotImplementedError
|
Loading…
Add table
Add a link
Reference in a new issue