Inform7 Writer -- Faster & Safer Code

Imagine a program, possibly a command line app, that writes the code for you. Leave out the language semantics, focus on the game. This is an example of a session (It’s a snippet, the routines to declare the game’s title and else have been left out):

shell>room
name>Observatory
desc>The observatory is a large room.[line break] There is a telescope in the middle.
location>east
from?>Hallway
Room declared.
shell> 

[Note: The user pressed enter after every line, except “Room Declared.”.]

Objects:

shell>obj
name>Telescope
primary desc>You can see a Telescope here.
desc>The telescope is white and large.
location>Observatory 

This is just an example, the real system would have more options (Defining kind(s), the alias, printed name, et cetera).

With something like this, the writer could create large and complex environments full of objects and scenery to interact with (Like the environment in Frederik Pohl’s Gateway; easily 40% of the rooms have more than 6 objects), in a short period of time (Introducing the concept of IF-RAD: Interactive Fiction + Rapid Application Development). A function could also be added to create simple tasks (IF, Instead of…, Instead of … when …, etc.) and to define the interaction between objects.

It works like this:

get input from the command line
if input is “room”
—ask questions to define room (name, printed name(?), description, directions)
------generate the code to do that (I7 code)
------write the code on a file
else, if input is “object”
—ask questions to define object (name, printed name(?), description, kinds)
------generate the code to do that (I7 code)
------write the code on a file

The App is called Inform7 Writer, and is still in alpha stage (Early development).

~~Eudoxia

Looks interesting. There’s another tool called Inferno that does almost the same but from a slightly different angle and smaller scope: http://groups.google.com/group/rec.arts.int-fiction/browse_thread/thread/2b336a9434aeb04

hmm, that looks interesting. Reminds me of this app for IF development, the name escapes me, but the subtitle in the readme file was “The Imagination Processor” (I think). Basically you go around, typing stuff like !edit or !goto (I think) to creatae objects and stuff.

Very immersive, but limited.

EDIT:

1 - There’s some progress with the functions to declare rooms and objects

2 - Here’s a screenshot of the program running, and the code behind it:

EDIT 2: I forgot:

The operating system is Linux, but the C++ code is portable, so any Windows or Mac user can compile it to make a native build.

The only part of the source code that should be changed for compatibility is the shell commands (system commands called to do something specific, for example: display the name and contents of the directory, create a new directory, navigate through the directories, and terminate the app if it goes into a loop and starts eating your RAM).

I tried something similar with a Word document template that allowed you to set up simple regions, rooms, doors, backdrops, objects and people using forms to organise things and then generate the basic code. People were cautious about using this sort of thing because of the use of macros. The template is still available on-line here if you want to have a look for ideas. It was created several I7 versions ago so some of the functionality may not be relevant now.

The link to it is:

prolixic.inform7.googlepages.com/Informatic.dot

Prolixic

Looks interesting, I might take a look at it (I think it might run on OpenOffice, meaby).

Some progress, finished the functions to declare basic info (author, name, release number, year, genre), the one to create rooms, and the last function to create objects, and the routines to write that info on a file (default is “generated_code.i7w”).

The code compiles, and runs.

Also:

I replaced cin >> var; with getline(cin, var); which is awesome.

EDIT:

I destroyed all the bugs (Mostly minor stuff) in the functions to declare rooms, objects and basic info.

I also created a function to create a new kind, (kind of thing, or kind of [something]), and a function to manually add code to the file that contains the generated I7 code.

Normally I would edit the last post, but the Alpha 0.2 version is here!

You can create rooms, objects, kinds, write raw code to insert it, and it includes some documentation.

The Linux executable is here:

megaupload.com/?d=QP5VI4AP

If you are running Windows or OS X, you can compile the source code with a C++ Compiler.

Sources:

[code]//i7 Writer
//Version 0.2, Alpha
#include
#include
#include <math.h>
#include
#include <string.h>
#include

using namespace std;

/Shell/
string input;
/Basic Info/
string title, author, genre, release, year;
/Room, Obj, Scn/
string name, desc, desc_if_present, loc, loc_from, printed_name, portable;
/Multipurpose/
string multip;

void splash()
{
cout << “========================\n”;
cout << “| Inform 7 Writer |\n”;
cout << “| ---------------------- |\n”;
cout << “| Faster & Safer Code |\n”;
cout << “| ---------------------- |\n”;
cout << “| By Alex Rosslyn, et al |\n”;
cout << “| ---------------------- |\n”;
cout << “| Type $h to get help or |\n”;
cout << “| $q to quit |\n”;
cout << “========================\n”;
cout << “\n\n”;
}

void bas()
{
/Code Vars/
string code_title_and_author, code_release, code_year, code_genre;
cout << “\ttitle>”;
getline(cin, title);
cout << “\tauthor>”;
getline(cin, author);
cout << “\trelease number>”;
getline(cin, release);
cout << “\tyear>”;
getline(cin, year);
cout << “\tgenre>”;
getline(cin, genre);
cout << “Basic Information Declared.”;
/Put all the info together/
cout << “\nConcatenating data…”;
/Title & Author/
code_title_and_author = “\x22”;
code_title_and_author += title;
code_title_and_author += “\x22 By \x22”;
code_title_and_author += author;
code_title_and_author += “\x22.\n”;
/Release/
code_release = "The release number is ";
code_release += release;
code_release += “.\n”;
/Story Year/
code_year = "The story year of creation is ";
code_year += year;
code_year += “.\n”;
/Genre/
code_genre = “The story genre is \x22”;
code_genre += genre;
code_genre += “\x22.\n”;
/TODO: Add the routine to ~w on file --(25/10/09: Done! File I/O is super easy.)/
cout << “\nCode genereated. Writing on file…\n”;
ofstream codegen_bas(“generated_code.i7w”, ios::app);
codegen_bas << code_title_and_author;
codegen_bas << “\n”;
codegen_bas << “[Basic Information]”;
codegen_bas << “\n\n”;
codegen_bas << code_release;
codegen_bas << code_year;
codegen_bas << code_genre;
codegen_bas << “\n”;
codegen_bas << “\n”;
codegen_bas.close();
/Print the generated code, telling the user that everything is allright/
cout << “<>\n”
<< “\x22” << title << “\x22 By \x22” << author << “\x22.\n”
<< "The release number is " << release << “.\n”
<< "The story year of creation is " << year << “.\n”
<< “The story genre is \x22” << genre << “\x22.\n”
<< “<>\n”;
/*
<< code_title_and_author
<< code_release
<< code_year
<< code_genre;
*
* This little function here ^ prints the value of the vars that
* contain the generated code to be printed on the file. Useful
* to see if the generated code printed on the screen matches the
* one written on the file.
* */
code_title_and_author.clear();
code_release.clear();
code_year.clear();
code_genre.clear();

}

void room()
{
string code_name, code_desc, code_loc, code_loc_from, code_printed_name;
cout << “\tname>”;
getline(cin, name);
cout << “\tdesc>”;
getline(cin, desc);
cout << “\tloc>”;
getline(cin, loc);
cout << “\t\tfrom?>”;
getline(cin, loc_from);
cout << “\tprinted name?>”;
getline(cin, printed_name);
if(printed_name == “DEF”)
{
goto room_final;
}
room_final:
cout << “Room declared.\n”;
cout << “Code genereated. Writing on file…”;
cout << “\n<>\n”
<< name << " is a room.\n"
<< name << " is " << loc << " from " << loc_from << “.\n”
<< “The description is \x22” << desc << “\x22.”;
if(printed_name != “DEF”)
{
cout << “\nThe printed name is \x22” << printed_name << “\x22.\n”;
/Printed Name/
code_printed_name = “The printed name is \x22”;
code_printed_name += printed_name;
code_printed_name += “\x22.\n”;
}
cout << “<>\n”;
/Name/
code_name = name;
code_name += " is a room.\n";
/Description/
code_desc = “The description is \x22”;
code_desc += desc;
code_desc += “\x22.\n”;
/Location/
code_loc = name;
code_loc += " is ";
code_loc += loc;
code_loc += " from ";
code_loc += loc_from;
code_loc += “.\n”;
/TODO: Add the routine to ~w on file/
ofstream codegen_room(“generated_code.i7w”, ios::app);
codegen_room << “\n”;
codegen_room << “[Room]”;
codegen_room << “\n\n”;
codegen_room << code_name;
codegen_room << code_desc;
codegen_room << code_loc;
codegen_room << code_loc_from;
if(printed_name != “DEF”)
{
/Routine to ~w the printed_name on the file/
codegen_room << code_printed_name;
codegen_room << “”;
}
codegen_room.close();
/Cout the generated code, for testing purposes
cout << code_name
<< “\n” << code_desc
<< “\n” << code_loc
<< “\n” << code_loc_from
<< “\n” << code_printed_name;
*
* Checked, the code is printed the way it should. The function will remain disabled
/
/Clear vars/
name.clear();
desc.clear();
loc.clear();
loc_from.clear();
printed_name.clear();
/Clear CodeGen Vars/
code_name.clear();
code_desc.clear();
code_loc.clear();
code_loc_from.clear();
code_printed_name.clear();
}

void obj()
{

string kind;
string code_name, code_desc_if_present, code_desc, code_loc, code_printed_name, code_kind, code_portable;
cout << "\tname>";
getline(cin, name);
cout << "\tdescription_if_present>";
getline(cin, desc_if_present);
cout << "\tdesc>";
getline(cin, desc);
cout << "\tloc>";
getline(cin, loc);
cout << "\tkind>";
getline(cin, kind);
/*Generate de Code -- Then scrap everything up and recycle*/
	/*Name*/
		code_name = name;
		code_name += " is a room.";
	/*Description If Present*/
		code_desc_if_present = "\x22";
		code_desc_if_present += desc_if_present;
		code_desc_if_present += "\x22.";
	/*Description*/
		code_desc = "The description is \x22";
		code_desc += desc;
		code_desc += "\x22.";
	/*Location*/
		code_loc = name;
		code_loc += " is in ";
		code_loc += loc;
		code_loc += ".";
	/*Kind*/
		code_kind = name;
		code_kind += " is a ";
		code_kind += kind;
	/*Printed name*/
		/*See ahead*/
	/*TODO: Add routine to ~w on file*/
	/*File ~w routine:*/
		cout << "Code [|]genereated. Writing on file...";
		ofstream codegen_obj("generated_code.i7w", ios::app);
		codegen_obj << "";
		codegen_obj << "[Object]";
		codegen_obj << "";
		codegen_obj << code_name;
		codegen_obj << code_desc_if_present;
		codegen_obj << code_desc;
		codegen_obj << code_loc;		
		codegen_obj << code_kind;
	/*End CodeGen 1*/
		cout << "\nanother kind?>";
			getline(cin, kind);
			if(kind == "DEF")
			{
				goto obj_next;
			}
			else
			{
				code_kind = name;
				code_kind += " is a ";
				code_kind += kind;
			}
	/*File ~w Routine:*/
		codegen_obj << code_kind;
obj_next:
cout << "\tportable?>";
cin >> portable;
if(portable == "Y" || portable == "y")
{
	/*CodeGen, Stage 2*/
		code_portable = name;
		code_portable += " is portable.";
}
else if(portable == "N" || portable == "n") 
{
	/*CodeGen, Stage 2*/
		code_portable = name;
		code_portable += " is not portable.";
}
/*File ~w routine:*/
	codegen_obj << code_portable;
	codegen_obj << ".";
/*End CodeGen 2*/
	cout << "\tprinted name(type 'DEF' to use obj name)>";
	getline(cin, printed_name);
if(printed_name == "DEF")
{
	goto obj_final;
}
else
{
	/*CodeGen, Stage 3*/
		code_printed_name = "The printed name is \x22";
		code_printed_name += printed_name;
		code_printed_name += "\x22.";
	/*Write it on the file*/
		codegen_obj << code_printed_name;
	/*End CodeGen 3*/
}
obj_final:
cout << "Object Declared.";
cout << "Clearing variables...";
/*Clear the Vars*/
	name.clear();
	desc.clear();
	loc.clear();
	loc_from.clear();
	printed_name.clear();
/*Clear CodeGen Vars*/
	code_name.clear();
	code_desc.clear();
	code_loc.clear();
	code_printed_name.clear();
	cout << "Closing file...";
	codegen_obj.close();

}

void scn(){}

void kind()
{
/CodeGen Vars/
string code_kind, code_parent;
/Routine/
cout << “\tname>”;
getline(cin, name);
cout << “\t\tparent?>”;
getline(cin, multip);
if(multip == “DEF”)
{
cout << “Kind " << name << " will be on the root location (No parent).\n”;
/CodeGen/
code_kind = name;
code_kind += " is a kind of thing.";
}
else
{
cout << “Kind " << name << " will use " << multip << " as parent kind.\n”;
/CodeGen/
code_kind = name;
code_kind += " is a kind of ";
code_kind += multip;
}
/~w it on the file/
/Clear Vars/
/Clear Code Vars/
/Close the Stream/

}

void life(){}

void ins()
{
cout << “\tcode>”;
getline(cin, multip);
ofstream codegen_ins(“generated_code.i7w”, ios::app);
codegen_ins << multip;
codegen_ins.close();
multip.clear();
}

void man()
{
cout << “\n\n--------------------------------------------\n”
<< “$h :: Show this document\n”
<< “$q :: Terminate application\n”
<< “$r :: Restart\n”
<< “$c :: Clear Screen\n”
<< “$d :: Show contents of current directory\n”
<< “$x :: Force Quit\n”
<< “$a :: About\n”
<< “------------------------------------------------\n”;
}

void about()
{
cout << “\n\n---------------------------------------------------------\n”
<< “Contents:\n”
<< " 1 $ About\n"
<< " 2 % About I7W\n"
<< " 3 % About the Author\n"
<< " 4 $ Manual\n"
<< “\n\n---------------------------------------------------------\n”;
main:
cout << “>>>”;
getline(cin, multip);
if(multip == “1”)
{
cout << “\n\n---------------------------------------------------------\n”
<< “Two Subsections:\n”
<< " 2 % About I7W\n"
<< " 3 % About the Author\n"
<< “\n\n---------------------------------------------------------\n”;
goto main;
}
else if(multip == “2”)
{
cout << “\n\n---------------------------------------------------------\n”
<< “About Inform-7 Writer (I7W):\n”
<< " Inform-7 Writer is a command-line application written in t\n"
<< “he C++ Programming Language with the GCC/G++ Compiler.\n”
<< " I7W takes input from a command-line interpreter, and asks \n"
<< “questions to the user to declare certain information. For exa\n”
<< “mple, if the user enters ‘room’ at the shell, the program wil\n”
<< “l ask him questions like ‘name’ and ‘desc’ (Description).\n”
<< " The answers are then put together along with Inform-7 code\n"
<< “and the code is written on a file.\n”
<< " With I7W, the writer can create huge and complex environme\n"
<< “nts for the player to explore, rich in objects and scenery.\n”
<< “\n\n---------------------------------------------------------\n”;
goto main;
}
else if(multip == “3”)
{
cout << “\n\n---------------------------------------------------------\n”
<< “About the Author:\n”
<< " Inform-7 Writer was created by Alex Rosslyn.\n"
<< " His blog can be found at:\n"
<< " http://pleasegodno.wordpress.com\n\n";
goto main;
}
else if(multip == “4”)
{
cout << “\n\n---------------------------------------------------------\n”
<< “Shell Commands (command::description):\n”
<< " bas :: Declare the Basic Information (Name, Author, etc.)\n"
<< " room :: Declare a room.\n"
<< " obj :: Declare an object.\n"
<< " ins :: Manually Insert Inform-7 code into the file.\n"
<< " kind :: Declare a new kind.\n"
<< " life :: Create a Lifeform (class::animal)\n"
<< “Internal Commands:\n”
<< " $h :: Show the Internal Commands\n"
<< " $q :: Terminate application\n"
<< " $r :: Restart\n"
<< " $c :: Clear Screen\n"
<< " $d :: Show contents of current directory\n"
<< " $x :: Force Quit\n"
<< " $a :: Show this Document\n"
<< “\n\n---------------------------------------------------------\n”;
goto main;
}
else
{
cout << “Invalid.\n”;
goto main;
}
}

int main()
{
/Vars/
/Header Text/
splash();
shell:
/Command Shell/
cout << “\nshell>”;
getline(cin, input);
if(input == “room”)
{
room();
}
else if(input == “obj” || input == “object”)
{
obj();
}
else if(input == “scenery” || input == “scn”)
{
scn();
}
else if(input == “bas”)
{
bas();
}
else if(input == “life”)
{
life();
}
else if(input == “ins”)
{
ins();
}
/Internal/
else if(input == “$q”)
{
goto EXT;
}
else if(input == “$h”)
{
man();
}
else if(input == “$r”)
{
splash();
}
else if(input == “$c”)
{
system(“clear”);
}
else if(input == “$d”)
{
cout << “Current Directory:\n\n”;
system(“pwd”);
cout << “Contents:\n”;
system(“ls -a”);
}
else if(input == “$x”)
{
cout << “Terminating process…\nTerminal should print ‘Terminated’ if successfull.\n”;
system(“killall i7w”);
}
else if(input == “$a”)
{
about();
}
/Invalid/
else
{
cout << “Invalid command.\n”;
goto shell;
}
goto shell;
EXT:
return 0;
}
[/code]

EDIT: Forgot to mention something: The generated code is stored in a file code “generated_code.i7w”. This is the default file (In the future you will be able to change the name).

EDIT_2: 1111th post in the section!

I don’t have developer tools installed on my Mac, so I haven’t tried this but, it occurs to me that Inform7 Writer could probably be implemented in I7 itself. Glulx games can output files, which means that you can output a fully functioning I7 source file directly. (I am doing this with a WIP, actually–the “game” allows you to create a composition interactively, then outputs source code that will recreate it in the player’s own project.) Just a thought.

–Erik

I think you just download xCode.

I think there were some commands in the I7 docs (‘show me’, I think) that allow you to get the properties of some object, and other commands to modify those.

Note: The original post said version .02, which is 0.02. This is a typo, it’s actually 0.2.

EDIT: The App that was similar to Inferno is called ‘CAW: Creative Adventure Writer’ and was developed by Richard Hunt.

Well, what I meant was that the whole application could be implemented in I7, with an I7 “game” that asks you “questions” and compiles your answers into source code, just as your WIP does. And now that I think about it, Emily Short did something very like this with her collaborative tool in the open collaboration on Alabaster:

http://emshort.wordpress.com/2008/10/29/a-halloween-experiment/

Heck, if you did something like this in I7, you could even generate the world dynamically–i.e., the author could interact with the world as she builds it.

Anyway, best of luck finishing up this tool!

–Erik

Thanks :wink:

I think the only functions that work flawlessly are bas (Basic game info) and room (Create a room). Obj (Creating an object) has this minor bug, and other functions (The one to declare kinds, the one to manually insert code, and the props one (Generates code like this: “A [kind] can be [text]”. Example: A thing can be green.)) also work flawlessly.

EDIT: YAY!

BUGS = KILLED :mrgreen:

Here’s a tip from a guy: Don’t write the whole program and then compile to fix the bugs, write 4 lines of code, then compile :mrgreen:

EDIT:

Example of session:

[code]========================

Inform 7 Writer
Faster & Safer Code
----------------------
By Alex Rosslyn, et al
----------------------
Type $h to get help or
$q to quit

========================

shell>bas
title>Millenium
author>Alex Rosslyn
release number>2
year>2001
genre>Science-Fiction/Horror
Basic Information Declared.
Concatenating data…
Code genereated. Writing on file…
<>
“Millenium” By “Alex Rosslyn”.
The release number is 2.
The story year of creation is 2001.
The story genre is “Science-Fiction/Horror”.
<>

shell>room
name>Observatory
desc>The Observatory is a large room.
loc>west
from?>hlw
printed name?>DEF
Room declared.
Code genereated. Writing on file…
<>
Observatory is a room.
Observatory is west from hlw.
The description is “The Observatory is a large room.”.<>

shell>room
name>hlw
desc>The Hallway is a long and dark room.
loc>east
from?>Observatory
printed name?>The Dark Hallway
Room declared.
Code genereated. Writing on file…
<>
hlw is a room.
hlw is east from Observatory.
The description is “The Hallway is a long and dark room.”.
The printed name is “The Dark Hallway”.
<>

shell>obj
name>Box
description_if_present>You can see a Box here.
desc>The Box is small.[if open] It’s open.[end if][if closed] It’s closed.[end if]
loc>hlw
kind>container
Code [|]genereated. Writing on file…
another kind?>openable
portable?>Y
printed name(type ‘DEF’ to use obj name)>DEF
Object Declared.Clearing variables…Closing file…
shell>ins
code>Include Basic Screen Effects by Emily Short.

shell>prop
a…>thing
can be…>green
A thing can be green.
shell>prop
a…>thing
can be…>red
A thing can be red.
shell>
[/code]