TextTron 3000™

by Ron Gilbert
Feb 09, 2016

Around a year ago, I posted some source code from Thimbleweed Park and was immediately demonized for including text in my source code. Cries of "How dare you!" quickly followed by readers covering the eyes of young ones.

Yes, text in source code is a really bad idea, but this isn't my first trip to the rodeo. I have a plan, a very cunning plan, a plan I have used many times in the past, a plan that I will now share with you.

First I need to state that I like text in source code, especially when in comes to adventure games. Text is an integral part of the game and it is woven into just about everything coded, from funny object descriptions to charming comebacks and moving dialogs.

Coding an adventure game is a very creative process. It's not cleanly divided into coders, designers and writers. I expect the people doing the game coding to be a little bit of all three of those things, it's why I like the text intermixed with the code. You can read the code and get a feel for what is happening. You can tweak code and text at the same time, getting everything to play just perfectly. You can't adjust one without the other.

But it does create a dilemma. If the game was in one language and never to be translated or voice recorded, you could blindly add text to your code without a care or worry, but that's not the world modern games live in.

Let's look at the process.  Here is some code from the Thimbleweed Nickel:

nickelMapWall =
    name = "empty frame"
    verbLookAt = function()
        sayLine("Very abstract... not a great use of color.",
                "...though I do like the subject matter.")
    verbPull = function()
        sayLine("It won't budge.")

We sit with the text and code like this for quite a while. There is no rush to exact it. Once exacted, you can still change it, inserting and deleting at will, but it's a little harder.

I wrote a tool in python called text_tron.py that does all the extracting (more on that later).  I run the tool...

text_tron.py --extract Nickel.nut

which transforms the above code into...

nickelMapWall =
    name = TEXT(0,"empty frame")
    verbLookAt = function()
        sayLine(PLAYER(0,"Very abstract... not a great use of color."),
                PLAYER(0,"...though I do like the subject matter."))
    verbPull = function()
        sayLine(PLAYER(0,"It won't budge."))

The lines have been extracted, but they haven't been assigned numbers or placed in the text database yet.

At some later point (days, not months), I'll run this...

text_tron.py --add Nickel.nut --tsv Text/ThimbleweedText.tsv

...and the code becomes...

nickelMapWall =
    name = TEXT(10087,"empty frame")
    verbLookAt = function()
        sayLine(PLAYER(10088,"Very abstract... not a great use of color."),
                PLAYER(10089,"...though I do like the subject matter."))
    verbPull = function()
        sayLine(PLAYER(10090,"It won't budge."))

...and a .tsv file is written (or added) to...

TEXT 10087 empty frame Nickel 581
PLAYER 10088 Very abstract... not a great use of color. Nickel 588
PLAYER 10089 ...though I do like the subject matter. Nickel 589
PLAYER 10090 It won't budge. Nickel 593

The file the line came from, along with the line number is stored to make sorting and printing recording scripts easier. It's good to know where the line came from.

TEXT and PLAYER are macros.

#macro TEXT($a,$b) "@$a:$b"
#macro AGENT($a,$b) "@$a:$b"
#macro PLAYER($a,$b) "@$a:$b"
#macro RAY($a,$b) "@$a:$b"
#macro REYES($a,$b) "@$a:$b"
#macro NATALIE($a,$b) "@$a:$b"
#macro POSTALWORKER($a,$b) "@$a:$b"
#macro CHET($a,$b) "@$a:$b"
#macro SHERIFF($a,$b) "@$a:$b"

They just transform each line of text from "Very abstract... not a great use of color." to "@10088:Very abstract... not a great use of color."

In the final build of the game, the #macros will get replaced with this...

#macro TEXT($a,$b) "@$a"
#macro AGENT($a,$b) "@$a"

...and the text will get stripped out, leaving only the line number.

when the game engine goes to display text, if it begins with '@', it knows a number follows and that is looked up in the text database and the (possibly translated) text is used.  During development, we include the text in the string so it can be updated, changed and checked against the database.

Rule #1 of Translation Club: Never ever ever edit the text in the database. If there is a change, make it to the source, then you run...

text_tron.py --update Nickel.nut --tsv Text/ThimbleweedText.tsv

...and any changes are moved from the source to the database.

Text is usually locked before sending it to the translators, so nothing should change after that. If it does, you make the changes in the source and run --update again. This will add the lines to the translation files as well.

Text databases can also be hotloaded, so a translator can edit the .tsv files, hit a key in the already running game and the new text is loaded and used. It should make translating easier.

I gave Boris the Nickel and he translated it and is still speaking to me, so I guess it went OK. Right Boris? Boris? Hello? Boris?

The extractor (text_tron.py) is pretty simple and driven by regex.  All of the regular expressions used are...

re_sayLineActor = re.compile("sayLine\\(\\s*([a-zA-z]+)\\s*,\\s*((\\\"(\\\\.|[^\"])+\\\"(\\s*,\\s*)?)+)\\s*\\)", flags=re.DOTALL)
re_sayLine = re.compile("sayLine\\(\\s*((\\\"(\\\\.|[^\"])+\\\"(\\s*,\\s*)?)+)\\s*\\)", flags=re.DOTALL)
re_quoteText = re.compile("(\"(\\\\.|[^\"])*\")", flags=re.DOTALL)
re_objectName = re.compile("name\\s*=\\s*(\"(\\\\.|[^\"])+\")")
re_addText = re.compile("([A-Z][A-Z0-9_]+)\\(\\s*([0-9]+)\\s*,\\s*\"((\\\\\"|[^\\\"])*)\"\\s*\\)")
re_FindMacro = re.compile("([A-Z][A-Z_0-9]+)\\(([0-9]+)\\s*,\\s*\\\"((\\\\.|[^\\\"])+)\\\"\\s*\\)")
re_yackOption = re.compile("^\\s*([0-9])\\s+(?![A-Z]+\\()\"?(.*?)\"?\\s*->", flags=re.MULTILINE)
re_yackSayLineCond = re.compile("^\\s*([a-zA-Z]+):\\s+(?![A-Z]+\\()\"?(.*)\"?(\s+\\[)", flags=re.MULTILINE)
re_yackSayLine = re.compile("^\\s*([a-zA-Z]+):\\s+(?![A-Z]+\\()\"?(.*)\"?", flags=re.MULTILINE)

These grab about 99% of the text. Every so often we doing something goofy and have to hand add the macros, but it would take me more than to write the extraction code than just change the game source. It's a small edge case.

"Wait... what about dialog?"

Good question...

That is why we have different #macros for each of the actors.  You notice that the actor name was placed in the .tsv file...

PLAYER 10090 It won't budge. Nickel 593
NATALIE 10091 Welcome to the Thimbleweed Nickel. Nickel 593

This serves no other useful purpose than to tag actors when exporting scripts.  Because there are five playable characters, some lines need to be read by five different actors.  When the script is exported, it sees PLAYER, and knows it need to emit scripts for RAY, REYES, RANSOME, and DELORES. When the audio is recorded, it will be saved into four files...


It's the same line (hence the 10090), but it's read by four different people.

"Hey, what about Franklin? Did you cut him from the game? I want my Kickstarter money back!"

No, he's not cut from the game, but he's a ghost and he has very different interactions, so we don't need to record him for most of the lines.

- Ron

Carlo Valenti - Feb 09, 2016 at 17:01
I do like coding too :)

Zak Phoenix McKracken - Feb 09, 2016 at 17:02

Easy and performing!!

Mattias Cedervall - Feb 09, 2016 at 17:11
Ron, has the font been completed? I haven't heard from Elwix in a while (since I got a beta version with Swedish characters). Great job, Ron!

Ron Gilbert - Feb 09, 2016 at 17:18
Yes, I got the final font from them. Looks great!

Mattias Cedervall - Feb 09, 2016 at 17:20
That's great! :-) I'm glad I told them about Thimbleweed Park and your desire for their font. Thank you for your reply, Ron! :-)

Christophe Pallarès - Feb 09, 2016 at 17:33
Hi Ron, can you let me know whether the game has already been localized, and if yes, which languages have been done so far? Thanks in advance!  :)

Ron Gilbert - Feb 09, 2016 at 17:35
The game has not been localized, we're a long way from being done with all the writing.

Christophe Pallarès - Feb 09, 2016 at 17:40
Oh, alright! When I saw the text in German, I thought that the process might at least have started... Thanks!  :)

Grafekovic - Feb 10, 2016 at 07:07
I don't know it it's the final translation, but I really like what is shown. I always prefer a translation that uses old-fashioned words like "Naseweis" over a more "up to date" translation. And since it's 1987 in the game, these words fit very well.

Simon - Feb 11, 2016 at 07:45
Funny thing. "Naseweis" was the exact word I immediatly hated, when I read it. Sound soooo... artificial. I know nobody who would say that to another person except of speaking to children maybe. Seems like you just can't make everyone happy :)
Still love the game (or what I have seen so far) and your blogposts! Thanks to the whole Thimbleweed-Team!

thiezn - Feb 09, 2016 at 17:59
The line numbers between 10087 and 10088 don't add up, there's something Ron doesn't want us to know! I smell a puzzle in there somewhere....

TEXT 10087 empty frame Nickel 581
PLAYER 10088 Very abstract... not a great use of color. Nickel 588

Christian - Feb 09, 2016 at 19:07
What happens if you edit text in the source code after the translation has already happened? Will the translation be discarded in the database? I think it might be a good idea to let the translator review the changes (e.g. an English typo was fixed => ignore. A word was changed => Change translation) and adjust or ignore the change to the original

Ron Gilbert - Feb 09, 2016 at 19:21
The "english" column gets updated and the "updated" column gets set you YES. So they can just sort the file and see any changes.

Big Red Button - Feb 09, 2016 at 19:55
1) As for the hotload function: Are the translators going to play through the whole game for self-monitoring?

2) Does the engine allow simultaneous dialog lines spoken by different characters?

vegetaman - Feb 09, 2016 at 19:56
Impressive Ron.  I have to agree that leaving stuff "together" and extracting it later is nice when you're constantly tweaking and changing stuff. But what is most impressive to me is those eye burning regular expressions you came up with. Very nice!

Ron Gilbert - Feb 09, 2016 at 19:59
They call me the "Regex Master".

Martin Wendt - Feb 10, 2016 at 06:57
That is equivalent to 'vodoo', is it not?
For our C64 point-and-click each line is stored in two languages in the script.
Not very efficient but I fully second your notion of texts belonging to scripts (at least during scripting).

Mario - Feb 10, 2016 at 15:33
Thats realy crazy. I use regex sometimes for extracting some code in html response pages but thats far from your regex. U r crank. I will ask u next time if i need a more complex one.

Zombocast - Feb 09, 2016 at 20:40
Increase the speed of the audio to match the text speed.

I want chipmunk agents!

Duphus - Feb 09, 2016 at 20:54
You text_tron.py simple!? Maybe it's that I'm a high schooler  who taught themselve simpler languages than  C++. Thanks for the Nightmares, Ron!

Zak Phoenix McKracken - Feb 10, 2016 at 04:28
With regular expressions you can do almost everything with text. It's simple, knowing the rules underneath that line of code.
Regular expressions are very, very important.
Like Wally's maps.

Zarbulonian - Feb 10, 2016 at 11:06
These Regexes are more complex visually than they are conceptually, because Ron is matching text with parentheses and strings (thus quotes and backslashes), which must be escaped with double backslashes. so "\\\\" matches a literal backslash, "\\\"" matches a literal quote, and "\\\"(\\\\.[^\"])+\\\"" matches a non-empty string:

"\\\"" matches the opening quote, followed by one or more (see the "+" suffix) of what is in the parentheses, that is,

- either an escape sequence ("\\\\.", a backslash followed by any character (that's what the dot means))
- or anything but a quote. "[^...]" is will match any character but those found in the brackets, following the carret. In this case "\"", i.e. a double quote. The pipe ("") character means "either".

At last, a final double quote.

Note to Ron: I'm not familiar with Python regexes, but I wonder if the quotes couldn't also be matched by just "\"" rather than "\\\"".

Wluut - Feb 10, 2016 at 12:15
That reminds me of one of the last xkcd comics:

Zarbulonian - Feb 10, 2016 at 12:44
I actually wrote a JS lib to mitigate this very issue: https://github.com/pygy/compose-regexp.js

Zombocast - Feb 09, 2016 at 21:28
Ron, I can tell you that I primarily program in VB.NET and and have an associates in computer science. My background is in key recording software and exporting to textfile and playing it back. Your code is easy to read and makes sense, thank you for the good read!

Peter Brodersen - Feb 09, 2016 at 22:55
At this point I'm a bit sad that the English language doesn't contain occasionally backslashes just for the fun of regular expressions.

Zak Phoenix McKracken - Feb 10, 2016 at 04:30
With regular expressions you can also catch the backslashes and any special character, if needed.
But why do you would have a backslash in the text?

Guga - Feb 10, 2016 at 00:40
I have to agree: for an adventure game, "hardcoded" text is important. Spoken lines are an integral part of the script and one must have a clear view of what's going on after an interaction. Or at least that's what I found when making my first game.

Then I wanted to provide a translation system, and that's where I extracted all the lines and replaced them with IDs... however, if I look at the code now, I see just horrible things like "say("ball.description1"); say("ball.description2");" and I can't think of programming this way.

What I'm doing now is hardcoding it anyway, prefacing it with a context (like say("ball.desc","My bowling ball"); say("ball.desc","I never lost a game with it");) then, at runtime, make the engine look up for the corresponding pair context/string in the language files for the current language. If present, it returns the translation. If not, it returns the English line just to be sure.

I'm lacking a script that extracts everything and automatically keeps the language files up to date, but for now that's not a problem. I don't have deadlines, that's (unfortunately) not my day job.

Guga - Feb 10, 2016 at 00:43
Reading my own comment I almost forgot: your system seems to address the context of the line (whether it is an object or a line spoken by someone) but not the context of the words themselves.

If you had to translate a simple word like "ball", how could you tell a translator if you're referring to the spherical object instead of a formal dance, without having him to play the game to find out?

Zak Phoenix McKracken - Feb 10, 2016 at 04:34
It should be fine if the translator could play tha game with the hot translation.

Guga - Feb 10, 2016 at 06:49
Yes, but the game is big :D how can the translator know where to find that particular line?

Zak Phoenix McKracken - Feb 10, 2016 at 11:14
I can only imagine... through a special menu, with direct shortcuts to specific room / scenes.

Ron Gilbert - Feb 10, 2016 at 11:18
There are debug menus that allow you to jump to any room in the game, there are also debug "scenarios" that you can invoke that set up the game at specific places so you don't have to play through the whole thing. But also save games.  Saving the game before something they are translating, then editing and reloading to start it again is quite useful.

Roman - Feb 10, 2016 at 03:18
"Naseweis"....ah yeah..:forgot it's 1986.....good that Boris and myself remember that time (and before). However, Boris, you need to use german orthography.from 1986....

longuist - Feb 10, 2016 at 06:38
Whats wrong with "Naseweis"?
Do you miss the ß?
Orthographically its derived from "wise", not "white".
So whether 1986 or the future (now), thats how it is spelled.

Grafekovic - Feb 10, 2016 at 07:10
Boris is in da house!

Roman - Feb 10, 2016 at 07:37
Nothing wrong with Naseweis....I didn't say it's wrong.......But I today it's not that commonly used....more likely "Klugschei**er"...or something totally different when I listen to my sons...Naseweis is ok since it's 1986 in the game.
The "orthography" remark was also meant as a funny note for Boris to generally stick in 1986 (and not today's orthography) ;-)
However I wonder what the Duden says about the case of "seltsames"......

Big Red Button - Feb 10, 2016 at 13:24
I agree with you. "Klugschei**er" is the most common synonym today, but I think it's too vulgar for an adventure game. So "Naseweis" is the perfect choice!

The word "Naseweis" always reminds me of a german poem by Anna Ritter, which I was annually ordered to listen to attentively on Saint Nicholas Day ("... was drin war, möchtet ihr wissen? Ihr Naseweise, ihr Schelmenpack - meint ihr, er wäre offen, der Sack?..."). Therefore it sounds extremely 1986-ish to me as well, even though the poem is a bit older.

By the way: "Schelmenpack" (archaic for something like "rascal scalawag") is also a nice word, albeit it's probably not in the game (yet). I like those "old-school" taunts, as they sound rather funny than harsh nowadays.

PS: It confirms the pleasing fact that the above translation is written by the same person who translated the first Monkey Island game into German. For instance, the general dealer on Méleé Island uses the word "Grünschnabel" (archaic for "greenhorn") when you enter his store. Maybe I'm about to over-interpret, but in my opinion there is a similar (subliminally humorous) language use. Anyway, I think that Boris is actually the best choice for the german translation!

longuist - Feb 10, 2016 at 13:47
Yes, Boris was and is the right one. Still not sure which version to play first. Probably flip a coin

longuist - Feb 10, 2016 at 13:37
Ok, thanks for clarification, then it was me who "shat wise" :)
I too would like to read (nowadays) uncommon swearwords like "Tunichtgut" ,"Dämlack" or "Xanthippe".
Because they are not so evil they dont need to be partly replaced with "tuna.."
Im really looking forward to the equivalent of the "root beer" -> "grog" translation :)

Zak Phoenix McKracken - Feb 10, 2016 at 04:31
How the "sprints" are going?

longuist - Feb 10, 2016 at 06:28
Wonder what Bogdan would have to say to the subject matter.

Bogdan Barbu - Feb 10, 2016 at 15:00
Wonder no more.

I've expressed my concerns earlier on the blog, the biggest of which is that a simple regex won't catch everything. Obviously, they can go in and manually edit stuff and if anything still gets omitted, it can be fixed with a post-release patch. It's not the end of the world by a long shot. The other, lesser, concern is that it's not flexible enough to handle dynamic text in interesting ways (even if TP doesn't use any, it's always a good idea to be forward-looking because it might save costs some day).

That said, I'm not sure what the point of the macros is. Might as well use the English strings as in-source keys directly and save a step. Regex would still be used for collecting the English strings, of course.

Development is quite often dirty and everyone is aware of that. Don't give more weight than necessary to my words because a negative code review (not that this was one) is not at all like saying something sucks and needs to be defended against the evil troll before everyone gets their feelings hurt. To me, it's more like shouting at a smoke detector for detecting smoke that you think is not dangerous (which may or may not be the case but the reaction is just as silly regardless).

Happy now?

Ron Gilbert - Feb 10, 2016 at 15:06
The macros are so the text can be compiled out of the game for final release without having the alter the source.  We don't use dynamic text due to the game being voiced. We need all the lines to be read as whole lines, otherwise the actors feel like a bad phone voice tree.

Nothing tends to get missed becasue I can set a mode where the system will no long accept pure text without throwing up warnings. It would only get missed in the testers never saw it, which is a whole different kind of failing. :-)

Bogdan Barbu - Feb 10, 2016 at 15:19
I am offended that you have the audacity to discuss these issues when I have already objectively decided that the game is an overall failure. :)

I've admited in that wall of text I wrote the last time that natural-sounding recordings make dynamic text a lot more complicated (even if I think there are some solutions worth exploring) but I'm really thinking more about things like text on a newspaper in a close-up influenced by events in the game.

Ron Gilbert - Feb 10, 2016 at 15:24
Splicing text together for things like that is particular hazardous due to translations. Different languages construct sentences very differently and the mablib approch sometimes doesn't work. If you're just splicing together whole paragraphs, then this system will work, that said, we do have something like what you describe in the game and I don't use this system, I've opted to have separate files of the large chucks of text that get translated since it is generated text.  As you've described, it is a special case.

Mattias Cedervall - Feb 10, 2016 at 15:27
"I find your lack of faith disturbing."

- Darth Vader

longuist - Feb 10, 2016 at 16:40
Stopped wondering. I slowly realize i awakened the sleeping dragon, but still cannot imagine the sheer enormity of the devastation.
After me the deluge :)

Julian Young - Feb 10, 2016 at 06:30
Oh my, at last, my dose of Ron Gilbert code crack. I'm saving this for lunchtime today!

unwesen - Feb 10, 2016 at 06:30
That seems like a slightly more complicated to use implementation of something like GNU gettext.

Geoffrey Paulsen - Feb 10, 2016 at 07:00
Great write up.  We use a very similar process for globalization of our High Performance Computing product.  Yes, many scream not to put English error messages in the code, but we did a similar translation.  Our process is simpler since we don't have different actors, we just have error codes and blobs of text.  Still it's a great system, and since the English strings get build into the final code, if something goes wacky and there are no error catalogs (translations to each other non-English language) we can always fall back on the built in strings.

One feature you didn't mention which you may want to consider adding right next to the --update would be a --verify.  Then if you're not certain if there have been changes made to either the database OR the source, you could verify one against the other.

Vilinthril - Feb 10, 2016 at 07:30
'cause I love to nitpick: There's a typo in the German text. “Seltsames” should be capitalized.

Big Red Button - Feb 10, 2016 at 18:15
Maybe owing to the reform of the orthography. But back in 1986 it definitely had to be lowercased.

Though, there should rather be periods after both "Mordfall" and "Plausch" instead of commas, because there are two main clauses in both sentences.

Big Red Button - Feb 10, 2016 at 18:26
I think I've got the hang of it: “seltsames” has to be lowercased due to the indefinite article. If there was a definite article instead, you would have been right.

Big Red Button - Feb 10, 2016 at 18:51
Okay, after a little investigation, I think I wouldn't vouch for my assumption! Maybe you're actually right. But in this case it's sheer nit-picking.

PS: There are several confusing rules due to the controversial reform in 1996. If anyone is interested in that reform, take a look at: https://en.wikipedia.org/wiki/German_orthography_reform_of_1996

Vilinthril - Feb 11, 2016 at 07:09
No. “Etwas” + adjective *always* has meant the adjective had to be capitalized, since it's a nominalized adjective.

Michael - Feb 10, 2016 at 08:37
And I always thought, that some of the regular expressions I come up with look complicated!

Andreas - Feb 10, 2016 at 08:43
Hi Ron, why did you decide to start every talking option with a dot?

Helge Frisenette - Feb 10, 2016 at 09:09
So you can have sentences more than a line long without it getting confusing.
OT I think I love you Ron.
I also think everyone is going to be surprised at how good this game is, and how little watching its progress has actually spoiled the surprise.

Deutschlehrer - Feb 10, 2016 at 09:44
Some early QA: The second comma in "Netter Plausch, jetzt gilt es, einen Mord aufzuklären." is wrong.

anon - Feb 10, 2016 at 14:34
As a native speaker I'd say the commas are just fine...

Mattias Cedervall - Feb 10, 2016 at 15:19
I'm from Sweden, but I'm good at Swedish. Most Swedes are bad at Swedish unfortunately. :-( They use English words despite the fact we have Swedish words and they use Swenglish. :-(

Leutschdehrer - Feb 10, 2016 at 16:11

Deutschlehrer - Feb 11, 2016 at 06:02
117/3 doesn't apply here and the sentence doesn't make sense with the second comma but nice try. Tell Boris or don't, my job here is done.

pit nick - Feb 10, 2016 at 18:16
I think you are wrong, both commas are correct.

Vilinthril - Feb 11, 2016 at 07:07
I'd prefer a semicolon in place of the first comma, but two commas is fine for me, too.

McDrake - Feb 11, 2016 at 08:07
Just asked my wife, as usual.
She studied  proofreading, so this time it made sense.

Her opinion: the second comma is correct.

Zarbulonian - Feb 10, 2016 at 10:18
Did you home bake the macro system?

Ron Gilbert - Feb 10, 2016 at 11:08
Yes, Squirrel has no pre-processor and I didn't want to have to run everything though the C pre-processor.

Zarbulonian - Feb 10, 2016 at 13:13
Interesting... Did you patch the Squirrel parser, or is it still a separate step?

Mischa Magyar - Feb 11, 2016 at 11:18
So all sentences of player characters are the same?
Or can there be different answers to the same actions by different actors?
(E.g. Ray saying "No way!" and Reyes saying "Nope!" or some similar stylistical differences.)

DZ-Jay - Feb 12, 2016 at 06:25
When reading the article and first encountering comments such as, "[y]es, text in source code is a really bad idea," my first thought was, "uh, what? how...? but, but, but... if the source has no text, isn't it a blank file? Do they write their code in emoji or something???"

LOL!  It took me a second or two to figure out that you guys meant dialog text. :)


lx - Feb 15, 2016 at 19:39
Hmm to be honest, this translation seems a little bit too word-by-word and artificial.
I'll admit I didn't grow up in the eighties, but I'm German and I think I have my legitimate doubts about this translation coming across as natural parts of a dialogue.
Just my copper...

P.s.: As far as the already here discussed world 'Naseweis' goes, for example... really? Did people really speak like that? I think we're talking uninspired, maybe bored, toughed-down and too-down-to-earth FED agents, not people who learned their vocabulary by reading theater-plays and reenacting olden fairy tales ; )

lx - Feb 15, 2016 at 19:45
p.p.s.: To be partially constructive... the word 'Klugscheißer' would be perfect, however I assume that there are objections because it may come across as too profane ( it really isn't, it's perfectly accepted slang on the weaker spectrum of profanity. Although it technically is profanity ). So... 'Schlaumeier' or an 'Da weiß aber jemand gut Bescheid...'  (sneaking the irony... a direct translation apart from 'Klugscheißer' might not be coming across too well, anyways) might also be an idea.. 'Naseweis', I don't know...

Topper - Feb 22, 2016 at 17:20
Ron, given I gave you all that money and all... do you think you could wing in a REGEX training puzzle in the game somewhere? I've NEVER gotten my head around it.

scott - Mar 09, 2016 at 15:10
i trust you ron.