Die Challenge “diehard” befasst sich mit dem gleichnamigen Film “Stirb Langsam” Dabei soll John McClane eine Bombe entschäfen, indem er Wasser innerhalb zwei verschieden großer Gefäße umfüllt.
Nach dem Verbinden zum Challenge-Server sehen wir zuerst jedoch nur folgende Nachricht:
rup0rt@lambda:~$ nc diehard.shallweplayaga.me 4001 Welcome John McClain... You are standing at the start of your adventure. You will be tested on your ability to solve a set of riddles in the minimum amount of time possible. As you glance around the room, you realize the problem you seek is to the north. To the west is a mysterious field. Exits: n w >
Offensichtlich müssen wir uns zunächst durch ein Labyrinth im Text-Adventure-Stil arbeiten, bevor wir die “Rätsel” lösen können. Da uns der Weg “im Norden” jedoch bereits vorgegeben ist, navigieren wir promt in diese Richtung.
> n
You go n
You are standing in a long dark hallway. Rays of light flicker off the smooth surfaces around you.
Exits: n s
> n
[...]
You go n
You see a large room with sleek black walls on every side. The ceiling overhead is smooth and devoid of features. In the center of the room is a small scale that links to a closed door.
A small fountain is gurgling water in the corner. The scale has a small inscription on it.
A red jug is sitting in the room.
A blue jug is sitting in the room.
A fountain is sitting in the room.
A scale is sitting in the room.
Nach mehreren Schritten, die jeweils nur eine Richtung ermöglichen, erreichen wir einen Raum, der uns alle Gegenstände für John McClanes Aufgabe bereitstellt. Mit dem Kommando “look” können wir dabei die Gegenstände genauer betrachten.
> look red jug You look at a red jug in the room. This shiny red jug appears to be a container made for holding water. A red jug holds 0 of 3 gallons. > look blue jug You look at a blue jug in the room. This shiny blue jug appears to be a container made for holding water. A blue jug holds 0 of 5 gallons. > look inscription You look at an inscription. It reads: To get to the next stage put 4 gallons on the scale.
Damit haben wir alle Informationen, die zum Lösen dieser Aufgabe erforderlich sind. Es handelt sich um die selbe Aufgabe, die auch John McClane zu lösen hatte. Wir sollen mit Hilfe von zwei Krügen, die jeweils fünf bzw. drei Gallonen fassen, ein Gewicht von vier Gallonen erzeugen.
Mathematisch betrachtet muss die Zahl Vier durch Addition/Subtraktion der Zahlen Fünf und Drei entstehen. Im vorliegenden Fall sieht die Rechnung so aus:
4 = 5 – 1
4 = 5 – ( 3 – 2 )
4 = 5 – ( 3 – ( 5 – 3 ) )
Wir müssen daher den großen Krug (5) in den kleinen (3) füllen um zwei Gallonen im großen Krug zu behalten. Anschließend füllen wir die zwei Gallonen in den kleinen Krug um – nach erneutem Befüllen und Umschütten – noch eine Gallone im großen Krug zu erhalten. Erneutes Befüllen und Umschütten fürt zu vier Gallonen im großen Gefäß.
Dies muss nun noch dem Spiel durch Senden der korrekten Kommandos vermittelt werden:
> get blue jug You get a blue jug. > get red jug You get a red jug. > fill blue jug You fill a blue jug with 5 gallons. A blue jug now has 5 gallons. > pour blue jug into red jug You pour 3 gallons into a red jug from a blue jug. A red jug is now full. > empty red jug You empty a red jug. > pour blue jug into red jug You pour 2 gallons into a red jug from a blue jug. A blue jug is now empty. > fill blue jug You fill a blue jug with 5 gallons. A blue jug now has 5 gallons. > pour blue jug into red jug You pour 1 gallons into a red jug from a blue jug. A red jug is now full. > put blue jug onto scale You put a blue jug onto a scale. The scale balances perfectly and a door opens to the next room!
Es funktioniert und die Tür zum nächsten Raum öffnet sich! Wie zu erwarten war, muss diese Aufgabe jedoch keinesfalls nur ein Mal gelöst werden. Auch die Größe der Gefäße variert und erfordert ein Python-Skript:
#!/usr/bin/env python import socket import time import re s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("diehard.shallweplayaga.me",4001)) toroom = "n\nn\nn\nn\nn\nn\ne\nn\nw\nn\n" getinfo = "l red jug\nl blue jug\nlook inscription\n" takeall = "get blue jug\nget red jug\n" # fetch data data = s.recv(2048) print data s.sendall(toroom) count = 0 goal = 0 while 1: print "COUNT: ", count data = s.recv(4096) print data if "You have failed!" in data: exit(0) if goal == 0 and ("A scale is sitting in the room." in data or "Streams of dust" in data): goal = 1 s.sendall(getinfo) continue for line in data.split("\n"): if "A key is sitting in the room." in line: s.sendall("look key\nget key\nl key\n") print "REQUEST SEND" data = s.recv(4096) print data break if "A red jug holds" in line: m = re.search('(?<= of )\w+', line) red = int(m.group(0)) if "A blue jug holds" in line: m = re.search('(?<= of )\w+', line) blue = int(m.group(0)) if "To get to the next stage put" in line: m = re.search('(?<= put )\w+', line) goal = int(m.group(0)) print "red=", red, "blue=", blue, "goal=", goal lred = 0 lblue = 0 solution = takeall if red > blue: while lred != goal: if lred == 0: solution += "fill red jug\n" lred = red solution += "pour red jug into blue jug\n" if lred > (blue-lblue): lred -= blue-lblue lblue = blue solution += "empty blue jug\n" lblue = 0 else: lblue = lred lred = 0 solution += "put red jug onto scale\n" solution += "drop blue jug\n" else: while lblue != goal: if lblue == 0: solution += "fill blue jug\n" lblue = blue solution += "pour blue jug into red jug\n" if lblue > (red-lred): lblue -= red-lred lred = red solution += "empty red jug\n" lred = 0 else: lred = lblue lblue = 0 solution += "put blue jug onto scale\n" solution += "drop red jug\n" solution += "n\n" print "Sending " + str(len(solution)) + " bytes..." s.sendall(solution) count += 1 goal = 0 s.close()
So wie eben ausführlich dargestellt, arbeitet auch das Skript. Es füllt die Gefäße so lange nach der Methode ineinander um, bis das Gewicht für die Waage entstanden ist. Dann wird das Gefäß auf die Waage gestellt, die Tür geöffnet und nach Norden gegangen.
(Am Ende muss der gefundene Schlüssel noch genommen (get) und betrachtet (look) werden. Diese Funktionalität ist mit dem Vorgriff auf das Wissen bereits implementiert.)
Die Ausführung des Skriptes wird zusätzlich dadurch erschwert, dass mit fortschreitender Zeit die Decke der Höhle einzustürzen beginnt. Das heißt, es kommt auch auf das Tempo an ;-). Nach einigen Anläufen war das Skript jedoch schnell genug und liefert diese Ausgabe.
You find yourself in a solid granite chamber filled with
hexadecimal writings on the walls. In the middle of the
room sits a small key.
A key is sitting in the room.
> get key
You get a key.
> look key
You look at a key that you are holding.
An inscription on the key reads:
The key is: yippie kay yay motherfucker 3nc83n89fg
Die Lösung lautet somit “yippie kay yay motherfucker 3nc83n89fg“.
Hi.
I’m not familiar with Python, I tried the same with PHP and every time I failed around ~ 16th round with timeout. The thing is socket by default has blocking mode, I couldn’t get all data till input easy. Nevertheless, non blocking mode didn’t give me any advantage as well.
So I wonder how the fuck your script is faster? :p s.recv(4096) doesn’t wait till buffer (4KB) is full? Or it gets data as soon as possible? In this case you could fail, because data probably contain only part of string like “A scale is si”.
But gj anyway!
Dude, I’ve tested your script right now, the same result:
>The roof collapses in on you. You have failed!
It highly depends on the Internet speed, it sucks tbh.
Hi,
you are absolutely right! I also had to run the script multiple times because it was “too slow”. It heavy depends on your connection and the server load. So this challenge is not very fair… :-/
s.recv(4096) fetches data from the socket up to a maximum of 4096. If there is less data it just fetches the amount waiting and stops blocking. That might be not the best solution (cf. select()) but I was lucky and could solve the challenge this way :-).