Langsam gehts ja los.
Die diesjährige Leaderboard ID lautet: 997136-bfa63c3e
Langsam gehts ja los.
Die diesjährige Leaderboard ID lautet: 997136-bfa63c3e
Advent of Code? Schau mal ob du hier fündig wirst!
Hm, Teil 1 in Bash hatten wir im Python-Forum auch gerade. Da war mein vorläufiger Endstand das hier:
Hier ist meine BASIC-Lösung für den C64:
10 TI$="000000":PRINT"READING INPUT...":DIM A(2000):OPEN 1,8,0,"INPUT01,S":N=0
20 INPUT#1,A(N):N=N+1:IF ST=0 THEN 20
30 CLOSE 1:PRINT N;"VALUES IN ";TI$:PRINT"PART 1..."
40 C=0:FOR I=0 TO N-2:IF A(I)<A(I+1) THEN C=C+1
50 NEXT:PRINT C;TI$
60 PRINT"PART 2...":C=0:FOR I=0 TO N-4
70 X=0:Y=0:FOR J=0 TO 2:K=I+J:X=X+A(K):Y=Y+A(K+1):NEXT:IF X<Y THEN C=C+1
80 NEXT:PRINT C;TI$
Ich weiss nicht, ist das auch ohne Spoiler-Tag schon kryptisch genug, oder soll ich so etwas besser verstecken?
Teil 2 ist ”naiv” gelöst, also einfach das was einem so offensichtlich dazu einfallen sollte. Das berechnet natürlich eine Menge unnötig mehrfach, das wäre also ein Ansatzpunkt um das zu optimieren. Die Laufzeit ist 3m25s wovon die ersten 51s für das laden der Daten drauf geht und Teil 1 ist in ca. 25 Sekunden durch.
Von der Python-Lösung zeige ich mal ungespoilert das Grundgerüst für den ersten Tag mit Unit-Test(s) aber ohne die konkrete Lösung, falls sich da jemand allgemeine Inspiration holen möchte:
#!/usr/bin/env python3
import sys
import pytest
def count_increases(values, window_size):
...
def main():
values = list(map(int, sys.stdin))
for window_size in [1, 3]:
print(count_increases(values, window_size))
@pytest.mark.parametrize("window_size, expected", [(1, 7), (3, 5)])
def test_count_increases(window_size, expected):
values = [199, 200, 208, 210, 200, 207, 240, 269, 260, 263]
assert count_increases(values, window_size) == expected
if __name__ == "__main__":
main()
Alles anzeigen
Ich lese Eingaben bei Advent of Code in der Regel einfach von der Standardeingabe, dann kann ich da mehrere ausprobieren und muss keine Dateinamen fest im Quelltext kodieren. Und aus den Beispielen in den Aufgabenbeschreibungen mache ich Unittests. Meiner Erfahrung nach decken die Beispiele in den Aufgaben fast immer auch die wichtigen Sonderfälle ab, bei denen man am ehesten Fehler machen kann, oder wo in der textuellen Beschreibung Mehrdeutigkeiten sind. Die Beispiele klären so etwas normalerweise.
Und hier das was in dem Listing oben fehlt — eine Import-Zeile und der Inhalt der `count_increases()` ist ein simpler Ausdruck:
Das was da aus einem externen Package importiert wird, kann man sich auch selber schreiben, beziehungsweise einfach aus der Python-Dokumentation kopieren. Da gibt es ja ein verwandtes Modul und in dessen Dokumentation gibt es ”Rezepte”.
Hallo,
ich freu mich, das ich Ähnlichkeiten mit __blackjack__ 's Code habe.
VORSICHT, es folgen meine vollständigen Lösungen:
#!/usr/bin/env python3
from pathlib import Path
from more_itertools import pairwise
DISTANCE_DATA = Path(r"C:\Users\Dennis\Documents\Advent_of_Code\2021_12_01.txt")
def read_sensor():
return [value for value in DISTANCE_DATA.read_text().split()]
def evaluate_sensordata(sensor_data):
return sum(1 for reference, compare in pairwise(sensor_data) if compare > reference)
def main():
sensor_data = read_sensor()
measurements = evaluate_sensordata(sensor_data)
print(
f"There are {measurements} measurements that are larger than the previous measurement."
)
if __name__ == "__main__":
main()
Alles anzeigen
#!/usr/bin/env python3
from pathlib import Path
from more_itertools import pairwise, windowed
DISTANCE_DATA = Path(r"C:\Users\Dennis\Documents\Advent_of_Code\2021_12_01.txt")
def read_sensor():
return [int(value) for value in DISTANCE_DATA.read_text().split()]
def evaluate_sensordata(sensor_data):
sorted_values = windowed(sensor_data, 3)
return sum(
1
for reference, compare in pairwise(
[sum(_) for _ in [sorted_value for sorted_value in sorted_values]]
)
if compare > reference
)
def main():
sensor_data = read_sensor()
measurements = evaluate_sensordata(sensor_data)
print(
f"There are {measurements} measurements that are larger than the previous measurement."
)
if __name__ == "__main__":
main()
Alles anzeigen
Mit der zweiten Lösung bin ich noch nicht zufrieden.
Grüße
Dennis
Da sind sie wieder, die mystischen und viel zu wenig von mir verwendeten Pakete (more)itertools und map. (siehe anderer Thread)
Das mit dem Pytest glaube ich habe ich in dem Thread schon letztes Jahr gefragt was es macht und dachte mir das will ich übernehmen. Denkste.
Und ich dachte es wäre die Stunde vom Slicing der Listen am Tag 1
Bin gespannt welche verschiedenen Lösungsansätze man noch im Dezember sehen wird.
Danke in die Runde für den Tipp zum Advent of Code. Das kannte ich noch nicht!
Na, dann probier ich es auch mal.
Hier mein Lösungsweg für Teil 2 von heute:
#!/usr/bin/env python3
import sys
import pytest
def get_new_pos(cmds):
horiz = 0
depth = 0
aim = 0
for cmd in cmds:
d, vs = cmd.split()
v = int(vs)
if d == "forward":
horiz += v
depth += aim * v
elif d == "down":
aim += v
elif d == "up":
aim -= v
return horiz, depth
def main():
horiz, depth = get_new_pos(line.rstrip() for line in sys.stdin)
print(f"horiz ({horiz}) * depth ({depth}) = {horiz*depth}")
EXAMPLE_LINES="""\
forward 5
down 5
forward 8
up 3
down 8
forward 2
""".splitlines()
# Solution: 15 * 60 = 900
def test_get_new_pos():
assert get_new_pos(EXAMPLE_LINES) == (15, 60)
if __name__ == "__main__":
main()
Alles anzeigen
simonz ``split("\n")`` zum Aufteilen in Zeilen ist nicht so praktisch denn dabei entsteht in der Regel am Ende eine leere Zeichenkette in der Liste, die man nicht haben will. Steht ja auch in Deinem Kommentar. Die `splitlines()`-Methode hat das Problem nicht. Und die erste Leerzeile kann man durch einen \ nach den """ unterdrücken.
Die Beispieleingaben binde ich in der Regel als Konstante so ein:
Dann gibt es keine extra Leerzeichen an den Zeilenanfängen und keine leeren Zeichenketten am Anfang oder Ende.
Dennis89 Bei Tag zwei ist in der Auswertungsfunktion die innerste „list comprehension“ (LC) überflüssig. Die macht ja im Grunde gar nichts ausser alles in einer Liste zu sammeln. Das ginge mit `list()` einfacher, aber auch das ist ja unnötig. Der Name `_` ist ja eigentlich für Werte die *nicht* verwendet werden. Und man hätte das mit `map()` kürzer schreiben können. Und auch hier ist die LC nicht nötig, ein Generatorausdruck hätte es auch getan.
Hofei Deine Navigator-Klassen enthalten redundanten Code. Das wäre eine Gelegenheit mal was mit Vererbung zu machen und eine gemeinsame Basisklasse aus den beiden heraus zu ziehen und nur noch die tatsächlichen Unterschiede in den beiden zu kodieren.
Die `move()`-Methode liesse sich aufgrund der dynamischen Natur von Python drastisch kürzen.
Ungetestet:
class BaseNavigator:
def __init__(self, horizontal=0, tiefe=0):
self.horizontal = horizontal
self.tiefe = tiefe
def move(self, befehl):
direction, value = befehl.split()
getattr(self, f"move_{direction}")(int(value))
class Navigator(BaseNavigator):
def move_forward(self, value):
self.horizontal += value
def move_down(self, value):
self.tiefe += value
def move_up(self, value):
self.tiefe -= value
class AimingNavigator(BaseNavigator):
def __init__(self, horizontal=0, tiefe=0, ziel=0):
BaseNavigator.__init__(self, horizontal, tiefe)
self.ziel = ziel
def move_forward(self, value):
self.horizontal += value
self.tiefe += value * self.ziel
def move_down(self, value):
self.ziel += value
def move_up(self, value):
self.ziel -= value
Alles anzeigen
Ich stelle meinen Code jeden Tag immer hier hin. Da gibt es auch ein Script day.sh mit dem man schnell und einfach einen existierenden Code eines Tages aufrufen kann.
Ich wuerde vorschlagen Ihr stellt ihn auch ins github oder sonstwohin. Ansonsten wird dieser Thread doch ziemlich codelastig . Bei Fragen oder Diskussionen zum Code kann man leicht Links zum Code hier posten wie z.B. diese Zeile wo ich wie __blackjack__ vorschlug - die Scripts so geaendert habe dass sie immer stdin lesen.
Hm. Ich mag Git/Github nicht wirklich.
Meine BASIC-Lösung ist heute schon deutlich länger als die gestern:
10 TI$="000000":MN=1000:DIM D(MN),A(MN)
20 OPEN 1,8,0,"INPUT02,S":PRINT"READ INSTRUCTIONS...":N=0
30 INPUT#1,L$:N=N+1:IF MID$(L$,LEN(L$)-1,1)<>" " THEN 1000
40 D$=LEFT$(L$,1):IF D$="F" THEN D(N)=1:GOTO 80
50 IF D$="U" THEN D(N)=2:GOTO 80
60 IF D$="D" THEN D(N)=3:GOTO 80
70 GOTO 1000
80 A(N)=VAL(RIGHT$(L$,1)):IF ST=0 THEN 30
90 CLOSE 1:PRINT"READ";N;"INSTRUCTIONS IN ";TI$
200 PRINT"FOLLOW INSTRUCTIONS...":X=0:Y=0
210 FOR I=1 TO N:ON D(I) GOTO 220,230,240
220 X=X+A(I):GOTO 250
230 Y=Y-A(I):GOTO 250
240 Y=Y+A(I)
250 NEXT:PRINT X;Y;X*Y:PRINT TI$
400 PRINT"FOLLOW INSTRUCTIONS WITH AIM...":X=0:Y=0:A=0
410 FOR I=1 TO N:ON D(I) GOTO 420,430,440
420 X=X+A(I):Y=Y+A*A(I):GOTO 450
430 A=A-A(I):GOTO 450
440 A=A+A(I)
450 NEXT:PRINT X;Y;X*Y:PRINT TI$:END
1000 CLOSE 1:PRINT"ERROR IN LINE";N:PRINT">";L$;"<":END
Alles anzeigen
__blackjack__ Danke für deine Tipps/Verbesserungen. Das ich '_' als Name verwendet habe, lag daran das ich keinen sinnvollen Namen fand und mir die Zeit ausging. (Daran hätte ich eigentlich merken sollen, dass da etwas überflüssiges gemacht wird).
Auch für heute fehlt mir bis jetzt noch etwas die Zeit. Das geht schon gut los ?
Hm. Ich mag Git/Github nicht wirklich
War auch nur ein Vorschlag. Kann ja jeder für sich entscheiden ?
Inspiriert von framp 's in #208 erwähntem Script day.sh habe ich mir auch ein Helferlein gebaut.
Es bietet neben diversen Kommandos eine optionale Mini-"IDE" (per tmux).
Ist ein nettes Programmierprojekt so nebenbei.
Bei Interesse:
#!/usr/bin/env bash
function help() {
cat << EOF
aoc - a little helper tool for the "Advent of Code"
Usage: aoc h|help this help text
l|ls|list [n] list today's or given day's file(s)
d|descr [n] edit today's or given day's description file
e|edit [n] edit today's or given day's script(s)
r|run [n] run today's or given day's script(s)
t|test [n] test today's or given day's script(s) with test-input
p|pytest [n] test today's or given day's script(s) via pytest
i|ide|tmux create a simple IDE with the help of tmux
EOF
}
if [ "$#" -eq 0 ] ; then
help
exit 1
fi
function run_scripts() {
echo "---"
for script in $scripts ; do
./$script < $input
echo "---"
done
}
if [ -z "$2" ] ; then
day=$(date +'%d')
else
# leading zeros are eliminated from input (due to octal system trap...)
# but there is no additional error checking yet (1..24, no-number etc.)
printf -v day "%02i" ${2##*0}
fi
input=day${day}-input
if [ ! -f "$input" ] ; then
echo "No input file '$input' found! Probably day ${day} has not been worked at."
exit 2
fi
scripts=$(ls -1 day${day}[ab].py)
chmod u+x $scripts
case "$1" in
i|ide|tmux) if [[ ! $(type -P tmux) ]] ; then
echo "tmux doesn't seem to be installed!"
echo "Can't create the IDE..."
exit 3
fi
# put current directory into PATH to be able to execute aoc without ./
export PATH=.:$PATH
tmux has-session -t AOC 2>/dev/null
if [ $? -ne 0 ] ; then
tmux new-session -d -s AOC
tmux split-window -t 0 -l 50% -v -d
tmux split-window -t 0 -l 50% -h -d
tmux split-window -t 2 -l 30% -h -d less -m day${day}*testinput
tmux send-keys -t 0 aoc ENTER
tmux send-keys -t 1 python3 ENTER
tmux send-keys -t 2 "aoc ls ${day}" ENTER
tmux new-window -n Description -d vim day${day}.txt
fi
tmux attach -t AOC
;;
l|ls|list) echo "Files for day ${day}:"
ls -l day${day}*
;;
r|run) run_scripts
;;
t|test) input=day${day}-testinput
run_scripts
;;
e|edit) vim -p $scripts
;;
d|descr) vim day${day}.txt
;;
p|pytest) pytest $scripts
;;
h|help) help
exit 1
;;
esac
Alles anzeigen
Hinweis: Das ist die erste Version (gewesen).
Korrekturen etc. pflege ich bei Github: https://github.com/gchriz/aoc
habe ich mir auch ein Helferlein gebaut.
Sieht gut aus Ich habe eben nur den Code ueberflogen und noch nicht getestet. Werde ich aber testen. Schade dass Du den Code nicht im github hast
Habe eben Deinen tmux Code downloaded. Da Du Python in Deinen Scripts nutzt und auch sonst noch ein paar Kleinigkeiten unterschiedlich sind zu meinem Environment musste ich noch ein paar Dinge anpassen. Aber jetzt funktioniert es auch mit meinen bash Scripts
Ich weiss nicht, ist das auch ohne Spoiler-Tag schon kryptisch genug, oder soll ich so etwas besser verstecken?
Ich kann nur für mich sprechen und bevor ich das entschlüssle versuche ich anders zu bescheissen ?
Gibts für heute eine Python-Lösung die nicht nur aus 'for'-Schleifen und '+=' besteht, sondern dem Niveau den Lösungen aus #203 entspricht?
Ich habe zwar für das Iterieren 'pairwise' genutzt aber danach nur aneinander gereite 'if'-Abfragen gefolgt von Rechenoperationen.
Ich habe aber das Gefühl, dass das schöner und eleganter geht?
Nur für heute habe ich keinen Kopf mehr und morgen gehts ja schon weiter ?
Grüße
Dennis
Ich habe was mit Operatorüberladung und `Enum` und nur ein ``if``/``else`` (also von dem ``if __name__ …`` abgesehen). Aber ansonsten jeweils eine ``for``-Schleife in den beiden Teillösungen über die Steueranweisungen.
#!/usr/bin/env python3
import sys
from enum import Enum
import pytest
from attr import attrib, attrs
EXAMPLE_LINES = """\
forward 5
down 5
forward 8
up 3
down 8
forward 2
""".splitlines()
@attrs(frozen=True)
class Position:
horizontal = attrib(default=0)
depth = attrib(default=0)
def __add__(self, other):
return Position(
self.horizontal + other.horizontal, self.depth + other.depth
)
def __mul__(self, value):
return Position(self.horizontal * value, self.depth * value)
class Direction(Enum):
FORWARD = Position(1, 0)
DOWN = Position(0, 1)
UP = Position(0, -1)
def parse_line(line):
direction_text, amount_text = line.split()
return (Direction[direction_text.upper()], int(amount_text))
def parse_lines(lines):
return map(parse_line, lines)
def follow_instructions(instructions):
position = Position()
for direction, amount in instructions:
position += direction.value * amount
return position
def follow_instructions_with_aim(instructions):
position = Position()
aim = Position()
for direction, amount in instructions:
if direction is Direction.FORWARD:
position += Position(amount) + aim * amount
else:
aim += direction.value * amount
return position
def main():
instructions = list(parse_lines(sys.stdin))
for follow_function in [follow_instructions, follow_instructions_with_aim]:
position = follow_function(instructions)
print(position, position.horizontal * position.depth)
@pytest.mark.parametrize(
"follow_function, expected",
[
(follow_instructions, Position(15, 10)),
(follow_instructions_with_aim, Position(15, 60)),
],
)
def test_follow_instructions(follow_function, expected):
assert follow_function(parse_lines(EXAMPLE_LINES)) == expected
if __name__ == "__main__":
main()
Alles anzeigen
dass das schöner und eleganter geht
Die Frage ist immer aus akademischer Sicht interessant. Aber ich kann immer nur empfehlen klaren und verstaendlichen Code - auch wenn er nicht elegant ist - zu schreiben. Ein jeder Code will gewartet werden und das ist meist nicht derjenige der den Code geschrieben hat Auch wenn man ihn selbst geschrieben hat - nach einer gewissen Zeit versteht man nicht mehr was man sich damals "elegant" ueberlegt hatte
framp Also für mich ist in ”elegant” auch ”klar und verständlich”. Wobei ich davon ausgehe, dass man die Spracheigenschaften und Bibliotheken ausschöpfen darf.
dass man die Spracheigenschaften und Bibliotheken ausschöpfen darf.
man bist Du .
Aber aus Maintenancegesichtspunkten wirst Du das meinst nicht mehr sein - speziell wenn Du gut bist. Du kannst nicht voraussetzen das Dein Nachfolger der Deinen Code maintainen muss denselben Erfahrungshoizont wie Du hast. Du kannst Dich natuerlich darauf setzen - so what - und die fuer Dich - und vielleicht auch objektiv betrachtete - eleganteste Loesung herausknobeln.
Aber wehe wenn Du mal Code maintainen musst den jemand geschrieben hat der noch besser ist als Du beim Ausschoepfen und Du musst einen Bug in dem ausgeschopften Code innerhalb einer Stunde fixen damit die Produktion wieder laeuft ... Dann bist Du froh wenn der Code nicht ausgeschoepft sondern schnell zu verstehen ist.
... ich habe das diverse Male erleben duerfen . Nobody is perfect und es gibt immer welche die mehr aus den moeglichen Sprachmoeglichkeiten ausschoepfen als Du. Und Du bist dann derjenige der wie Ochs vorm Scheunentor steht wenn er geaendert werden muss.
Nimms bitte nicht persoenlich - das Du steht hier fuer jeden der kodiert
Das sehe ich nicht so. Du setzt hier IMHO Sprachumfang und Bibliothek ausschöpfen und elegant zu sehr mit unklar und unverständlich gleich. Die Spracheigenschaften und Bibliotheken sind doch gerade dazu da um klareren und leichter verständlicheren Code zu schreiben. Wenn man das nicht macht, dann endet das in Code der den kleinsten gemeinsamen Nenner von einem Querschnitt von prozeduralen Programmiersprachen verwendet, und keine Bibliotheksfunktionen verwendet, die Sachen einfacher machen und gut getestet sind. Die einzelnen Anweisungen mögen auf diese Weise verständlicher sein, aber es sind dann halt deutlich mehr für die gleiche Aufgabe, es muss also auch mehr Code gelesen und verstanden werden, und es werden dann auch gerne mal Fehler gemacht, die man durch Bibliotheksfunktionen hätte vermeiden können.
Zum Beispiel bei Tag 1 hatte ich in Teil 2 bei meiner BASIC-Lösung einen typischen „off by one“-Fehler und am Ende des Arrays ein Element zu viel addiert. Im Python-Forum hat das auch jemand in Bash gelöst und hatte einen „off by one“-Fehler am Anfang des Arrays gemacht. Das kann einem mit `pairwise()` und/oder `windowed()` in Python nicht passieren. Und wer diese Funktionen nicht kennt, kann in deren Dokumentation nachlesen was die machen, inklusive Beispielen wo man Argumente und Ergebnisse sieht. Das ist ja nicht irgendwie undurchsichtiges Hexenwerk oder Geheimwissen. Es ist eleganter, weil einfacher und weniger fehleranfällig als selbst geschriebenes jonglieren mit verschachtelten Schleifen und Indexzugriffen mit zusätzlichen Offsets.
Man kann nicht bei jedem den gleichen Erfahrungshorizont voraussetzen, aber doch auch nicht bei jedem Programm vom Programmieranfänger ausgehen, der nur das erste drittel des Anfängertutorials durch hat. Bei Python dürfte ich dann keine Klassen schreiben, keine Ausnahmen behandeln, oder gar auslösen, und müsste in den wenigen Funktionen die neben dem ganzen Code auf Modulebene stehen, auch noch ``global`` verwenden. ?
Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!