What is ZIL anyway?
blog.zarfhome.com
blog.zarfhome.com
What is interesting in particular is how it possibly reveals their setup: they had a full MDL interpreter running on their workstations, and ZIL is a subset that could be cross-compiled to a stripped-down target: the Z-machine.
It looks like ROUTINE, OBJECT, ROOM and such are macros (in sources unknown) that would define standard MDL objects, possibly stored in some structure for findability. Then, the cross-compiler would be a program that takes these objects and writes out some Z-code. The workflow probably allowed (1) playing the games directly in the interpreter without compilation (2) making small changes to a running game and (3) recompiling the Z-code after having made those small changes, without needing to reload all the game's source code.
Except for the cross-compilation step, this is the sort of environment the original Zork would have been written in, and I find it hard to imagine they would have accepted anything less.
NLP etc. has great potential to improve the parsing capabilities, to be sure, but it's not obvious to me that that will dramatically improve the experience. Is `pick up the shiny brass thing` really better than `GET LAMP`? IMO, it might make IF more accessible to non-techy, non-text parser gamers, but that's about it.
I think the next leap for IF games will involve non-linear stories that are better suited to the medium. The success of games like "Papers, Please" in particular has sparked my imagination in this area.
That is an incredibly ambitious concept for a game jam, but it sounds like they pulled it off?
(Note: Parent comment edited risky -> ambitious.)
Seriously, I'm so tired I looked up "ballsy thesaurus" and clicked related words for probably five minutes before I gave up looking and just typed 'risky' hoping it'd be good enough.
The gameplay video is awesome.
But perhaps not so much NLP... this was 2012! Was it really that long ago? And my memory was fuzzy.
But just imagine what could be possible!
The only way to win was to slip one lie into the retelling, and then when the game catches up to the present moment...
[1]Spider and Web
If you don't make moon logic puzzles, your puzzles can be solved relatively quickly unless you're very, very good at making fair puzzles which are also legitimately difficult. Therefore, you're going to need more puzzles than an FPS or action game which incidentally has some puzzles in order to achieve the same total game length, and making content like that is legitimately hard.
Similarly, in order to make your games playable more than once, you need branching paths, such that you have different routes for the players to go down each time, and I really shouldn't need to explain the concept of "combinatorial explosion" in this forum. Add to that the expense of things like art assents and voice acting (unless you really think text adventures are going to come back in a big way) and it becomes prohibitively expensive to not railroad everyone down one main plot with, at most, a few minor side-treks which quickly rejoin the main line.
I'm not going to go down some half finished path in Bandersnatch when Black Mirror has enough problem with single stories.
The overall storyline is replaced with a repetitive but novel task with increasing difficultly, and various other loosely coupled storylines are grafted onto the task in different ways. The task also replaces the explicit puzzles.
It is as though rather than being told the story, you are viewing vignettes of the story through a very narrow window
The point about AI in IF would be to break that mold. Dialogs can be more challenging, for example you could make a game about interrogating people. Or you could tell NPCs to do certain stuff for you. Or these NPCs could have some advanced planning or decision making.
To make IF more interesting, especially for those outside a very narrow niche, you would need them to be a lot easier to navigate, and offer more challenges that don't look like puzzles and don't hit you with a brain-twister at every turn.
I don't follow the IF world closely (though I often want to set aside the time to get into it), but surely they are also heavily focused on the prose too?
If you treat the text as just an interface to get to the underlying formal game mechanics, then sure, IF is just simple puzzles because there's no hand-eye coordination going on.
But if you treat the text as a fundamental part of the experience, then the puzzles are only a small piece.
By analogy, films are the world's shittiest games because — even though the graphics are nice – the gameplay is obviously pretty meager. The player doesn't get to make any choices at all! But with a film, the graphics are the point.
The one benefit of text games over other types of games is that you can essentially implement whatever world you want, without a trillion Dollar budget for graphics and sounds effects. A holo-deck powered by imagination.
Speaking of games in holo-decks... The killer-app for AI-powered IF may be erotic fiction.
This debate has been going on for decades. (See Photopia.)
Though I'd guess that's more of a sophisticated GOFAI than RNNs.
Verbose: Always show the full description of a room when you enter it.
Brief: Show a short description when you re-enter a room.
Superbrief: Always show the short description.
(Regardless of which you chose, you could always 'look' to get a full description.)
What if the world included a number of fully independent agents with their own motivations, values, and policies and equipped with pattern matching, goal solving, optimization, and even a hint of common sense[1]. The game state would never be the same twice, and it would depend on how you interacted with the agents and they with each other.
TlDr: If you open a door, you might let the Grue out of its maze.
I don't believe "complex and dynamic" NPCs would work well enough to make a fun game.
You could probably make it work now with, say, a game world full of broken robots or something.
Both speech recognition and speech synthesis have progressed by leaps and bounds. Computer graphics have progressed to a point where human characters can be made expressive and nuanced.
Imagine an engine that that takes games scripted as interactive fiction, procedurally generates the game world from the prose (far fetched, but first steps exist as research papers), populates it with the characters from the text and makes them act out the written dialog while honoring the implicit stage directions given in the written form ("he whispers in your ear" etc...). All of that presented in VR, which would make having a dialog with these virtual characters more natural.
Looking at the tech that exists today, I believe that we are about halfway there.
I also have some internal Infocom emails and photos and things. Not sure if posting those would be welcome by the participants since most of them are still alive.
Genuinely curious... is it okay to post private details of a person once they're dead? There are things that I want to keep private while I'm alive, and that I hope stay private when I'm dead. Does other people's curiousity trump my wishes just because I'm no longer around?
In the US, it's reasonably well established that you have no privacy rights after death[1].
That doesn't necessarily make it safe to publish details about someone after they are dead though as property rights do exist through the estate of the deceased, so you may have to end up defending yourself from a defamation suit.
In terms of it being "okay" there are unwritten rules about allowing some not-well-defined period of mourning before publishing private details, particularly ones that reflect poorly on the deceased.
Publishing e.g. private correspondence of people from just 100 years ago seems completely non-controversial though.
https://waxy.org/2008/04/milliways_infocoms_unreleased_seque...
edit: if I had access to that data, I'd be interested in finding any hidden source files -- there's a copy of the source to a version of the ZAP assembler hiding in one of the minizork repos Jason Scott posted, for example. Might there be some others?
Actually kinda serious. You need to be able to spare the RAM to have the comments, whitespace, large variables, breaking things nicely into functions, etc. I've got modules I've documented where the documentation alone would fail to fit into, say, a Commodore 64's RAM, as plain text. Early 1980s "big iron" would still be considered on the high end of embedded programming today. (In the sense that a Raspberry Pi is embedded programming.)
8MB for an embedded controller is no great ask and you have to crawl a ways down the spec sheet pile to get a 33MHz processor nowadays. A top-end PDP-10 would today put it roughly on par with an Arduino [1], although the Arduino would be a bit short on RAM.
Even if you have the space to spare on comments and long variable names, you've been raised in a culture that considers those insanely expensive extravagances.
The indentation and all-caps are not modern, and the MACLISP primitive names like TERPRI are unchanged from the 60s. Otherwise, the coding and commenting style would not really raise any eyebrows today.
I first learned of literate programming from this culture instead of Knuth, e.g. https://dspace.mit.edu/handle/1721.1/41983 ("The fourth section presents a completely annotated interpreter for AMORD").
I'm just saying yeah, expectations have definitely evolved, but it's not the case that PDP-10 hackers back then had not yet discovered or valued readability.
It's less fringe today, but I'm still not sure "code should be optimized for reading, not for writing" isn't still a fringe position, at least as evidenced by what people's actions say about their beliefs, rather than their words. Lip service of that is probably at least not fringe any more, which is progress of its own sort.
I'm not saying they were into literate programming, I'm saying it's misleading here to talk about what coding on the C64 was like.
Spaces were for suckers. Each line had a few bytes overhead so you put as many statements on each line as you could. You stuck to 1 character for your common variables (only 2 characters were significant anyway). You used the abbreviated name for commands. (This was around 81, 82.)
Something like this:
> 10 FORT=1TO10:?"IHN!":NEXTT:FORT=1TO1000:NEXTT:GOTO10"
This is from memory, so probably not actually correct code but this is meant to print "IHN!" 10 times, wait about a second and then repeat. T is used as a FOR loop variable twice.
<+ 1 2> ↦ 3
'<+ 1 2> ↦ <+ 1 2>
(+ 1 2) ↦ (+ 1 2)
<TYPE <+ 1 2>> ↦ FIX
<TYPE '<+ 1 2>> ↦ FORM
<TYPE (+ 1 2)> ↦ LISTIt's a very useful distinction. In regular Lisp, lists are the only data type that is not self-evaluating. This leads to confusion because the serialization of a list can mean different things depending on context. Consider:
(eval (add 1 2))
(eval '(add 1 2))
Those forms both produce the same result when evaluated. It happens by two very different execution paths, but this is not immediately apparent when you run this code. So a beginner may well be surprised to learn that the following: (eval x)
(eval 'x)
may or may not produce the same result. So it's genuinely helpful to have two types of lists, one of which is self-evaluating, and the other which isn't, and to have syntax that distinguishes the two.Another example: all of these produce the same result:
(eval (add 1 2))
(eval (eval (add 1 2)))
(eval (eval (eval (add 1 2))))
and so on. But this is true only because (+ 1 2) evaluates to 3 which is self-evaluating. Contrast that with, say: (eval (cons 1 2))
which immediately produces an error. Trying to figure out why "add" "works" and cons doesn't and how to "fix" this can be very frustrating for a beginner. (1 <+ 2 3> 4) -> (1 5 4)
'(1 <+ 2 3> 4) -> (1 <+ 2 3> 4)
'<1 <+ 2 3> 4) -> <1 <+ 2 3> 4>
<1 <+ 2 3> 4> -> an error (probably that 5 is not structured)
Parens give list literals like in most modern languages.An interesting thing about MDL is that both FORM and LIST have the same "PRIMTYPE" so all the usual lisp-like list manipulations apply to both. A strange thing is the way these are implemented: pretty much every object (with fixed-size allocation) has a NEXT pointer, and a FORM or a LIST is an object that has a pointer (possibly null) to the first object. Taking the tail of a list entails allocating a new LIST object that points to the second element.
You may wonder: how can an element be part of two lists at the same time? Well, you copy the object since it's in a 36-bit word. (Objects that do not fit in such a word are put in lists using a special DEFER object that points to it.)
https://github.com/taradinoc/mdl-docs/blob/master/docs/appen...
What this is doing is binding the parentheses to a list constructor. I.e. (1 2 3) is really something like <list 1 2 3>. It's more like a quasiliteral (like Lisp's backquote).
Unfortunately, the fact that the printed representation of a list is the same as the code which calculates it makes things somewhat "muddled", pun intended.
What if we have this: '(1 2 3 (4 5 6)). What is (4 5 6) here? It can't be the list (4 5 6), because (4 5 6) is code that constructs that list. Using the same printed rep for both is a very bad idea.
In MDL, parens and angle brackets denote LISTs and FORMs, respectively. These are both objects with the same PRIMTYPE (a linked list) but are tagged differently. The evaluator evaluates these objects differently from each other: a LIST has each of its elements evaluated, with the results put in a new LIST. It truly is distinct from a FORM that has the same effect.
Distinctions like this were designed to make the corresponding parts of Lisp (like what you mentioned) less confusing.
Indeed, in your quote example, that (4 5 6) is a LIST, not a FORM, avoiding the very bad idea. Quasiquotation pretty much doesn't need to exist because of the ! sequence "operator" for splicing lists into lists and forms.
Hmm... I guess I don't know everything quasiquoting is used for in Lisp, but in MDL and ZIL, segments don't really solve the problem that quasiquoting does when it comes to writing macros.
That is, I usually want to use macros to generate code according to a template:
<PRSI? ,FOO>
should become <==? ,PRSI ,FOO>
The way to write that macro is to call FORM to build the form: <DEFMAC PRSI? (X)
<FORM ==? ',PRSI .X>>
But if it's a complex block of code, and the blanks I want to fill in are deeply nested, the template quickly becomes unreadable, because every structure turns into a form, and every form turns into a call to FORM.As an example, consider the following possible implementation of PROG1 in terms of LET's implicit PROGN. They both evaluate a sequence of forms in order, but the first returns the value of the first form, and PROGN the last:
(DEFMACRO PROG1 (form1 &REST forms)
(LET ((temp (GENSYM)))
`(LET ((,temp ,form1))
,@forms
,temp)))
A MDL equivalent would be something like (though I admit I'm entirely going off documentation!): <DEFMAC PROG1 (form1 "ARGS" forms "AUX" (temp (ATOM "temp")))
<FORM PROG ((.temp .form1))
!.forms
.temp>>
Without quasiquotation, the Lisp example would be (without capitalization this time---in Lisp symbols are auto-capitalized): (defmacro prog1 (form1 &rest forms)
(let ((temp (gensym)))
(list* 'let (list (list temp form1))
(append forms (list temp)))))
I chose this example to demonstrate how sequences alleviate the pain of splicing things into the middle of things.Backtick is not used in MDL, right? And you're somewhat free to extend the MDL used in Zilf, right? You could get the best of both worlds by adding either a PREFORM type or an REBUILD form. For example:
`< ... > or `< ... `> -> #PREFORM ( ... )
or `struct -> <REBUILD struct>
The semantics would be that a PREFORM would be evaluated like a LIST, but then it would CHTYPE to FORM. Or, a REBUILD is an FSUBR that expects a structure, and it maps EVAL over the elements of that structure.The macro would look like
<DEFMAC PROG1 (form1 "ARGS" forms "AUX" (temp (ATOM "temp")))
`<PROG ((.temp .form1))
!.forms
.temp>>
I don't know all the consequences of this change to MDL, though.https://github.com/PDP-10/muddle/blob/master/mim/development...
It doesn't run on any available MDL implementation, because it's actually for MIM, an even more obscure and undocumented extension of MDL. ZILF implements some parts of MIM, but doesn't implement READ-TABLEs at all, and the format of the table implied by that code is different from what's documented for MDL.
A translation from MDL to Python would look like this:
<a b c> -> a(b, c)
(a b c) -> [a, b, c]
(This is in contrast to a sibling comment: in MDL (<+ 1 2> <+ 3 4>)
would evaluate to (3 7)
)However, there is a bit of a caveat in the translation. In MDL, atoms are self-evaluating, and it takes a call to LVAL or GVAL to obtain their stored values (as a shorthand, dot and comma prefixes give LVAL and GVAL calls, respectively). I haven't been able to find out from exactly which scope the evaluator gets the definition of a function. Which of the following two corresponds to what the evaluator actually does?
<a b c> -> <.a b c>
<a b c> -> <,a b c> <DEFINE FOO () ...>
is equivalent to <SETG FOO <FUNCTION () ...>>
But EVAL will fall back to the LVAL if the atom doesn't have a GVAL. In other words: <A B C>
is equivalent to <APPLY <COND (<GASSIGNED? A> ,A)
(<ASSIGNED? A> .A)
(T <ERROR>)>
B
C>
https://github.com/taradinoc/mdl-docs/blob/master/docs/03-bu...Another thing I couldn't find is exactly what a trailer with an empty OBLIST means. Does it use <ROOT>? It shows up in TELL for the uses of RETURN!-, which probably are to use a non-overridden version of RETURN.
(Or any idea what SETG20 is supposed to mean? It shows up in Bureaucracy.)
I'm not sure what the purpose of things like <RETURN!-> and <ORB!-> is. It could have something to do with the way they implemented Z-machine opcodes as MDL atoms, i.e. <RETURN!-> forces it to use the MDL SUBR instead of the opcode? Maybe they moved the MDL atoms that collided with Z-machine opcodes to a separate OBLIST, and this was needed to access them from MDL code in the same file?
I never figured that part out, so when ZILF is generating code, it uses the same OBLISTs it used while interpreting and just looks things up by their PNAMEs.
SETG20 and DEFINE20 are used in "MDL-ZIL" files, where routines are defined with DEFINE instead of ROUTINE, global variables are created with SETG instead of GLOBAL, etc. Presumably that was a way to run the games in MDL during development to avoid recompiling them. SETG20 and DEFINE20 are aliases for the MDL versions of SETG and DEFINE.
<SETG X 0> ;FORM
(NORD BERT) ;LIST
[1 MONEY 2 SHOW] ;VECTOR
"Bracket-ish" ;STRING
{ what goes here? } ;TEMPLATE
Then you got your exciting brackets, where the first ! is mandatory but the second is optional: !<REST .FOO!> ;SEGMENT
![2 4 8 16 32 64!] ;UVECTOR
Then you got your hashtag brackets, which pop up in the corner of the TV screen reminding you that there's another way to CHTYPE: #FORM (+ 1 2) ;"same as <+ 1 2>"
And your hashtag voucher brackets, which are really just defective angle brackets you can exchange for a free hashtag: <> ;"evaluates to #FALSE ()"
Finally, you got your bogus brackets, things that look like they could be a new kind of exciting brackets, but when you look closely they're just prefix operators that tend to appear before brackets: '(OSCAR WILDE) ;"same as <QUOTE (OSCAR WILDE)>"
%<+ 1 2> ;"evaluate immediately"
%%<CRLF> ;"evaluate and discard result"
See also: https://github.com/taradinoc/mdl-docs/blob/master/docs/07-st...