diff --git a/.gitignore b/.gitignore
index 719f954..c935307 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,7 @@ ui/uitest
 /missing
 /compile
 *~
+*.swp
+*.info
+TAGS
+tags
diff --git a/ChangeLog b/ChangeLog
index c29935b..f0ee518 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,86 @@
+2020-07-17  Matt Henry  <mcthenry+gnu@gmail.com>
+
+	* 1.3.0: bumped the version to 1.3.0
+
+	* GUI: updated GNU Robots to GTK3
+
+	* Scheme/Build: Configured to prefer Guile 2.2 and fall back to
+	2.0.
+
+2020-07-07  Matt Henry  <mcthenry+gnu@gmail.com>
+
+	* overhaul2 branch changes as of 2020-07-07:
+
+	* Code: Refactored code to reduce dependencies (much less of a web
+	of #includes)
+
+	* Code: Removed unneeded imports
+
+	* Code: Compiler warnings reduced through appropriate casting
+
+	* Code: Marked functions as static if only used in local .c file
+
+	* Code: Refactored file searching in main.c into a function using
+	the environment variable and the #define from configs.h
+
+	* Code: G_ADD_PRIVATE being used for private data throughout
+	(eliminates compiler warnings for deprecated features and
+	standardizes on latest method).
+
+	* Code: Signals are now the only means of communication between the UI
+	and the Map and Robot objects (no calling from unexpected threads)
+
+	* Commandline: modified GNU Robots to use GOption commandline
+	processing.
+
+	* Commandline/GTK: modified GNU Robots to accept (and process)
+	standard GTK commandline options.
+
+	* GUI: Added a menu bar containing File/Open Map; File/Quit;
+	Scheme/Interrupt and Help/About
+
+	* GUI: user can load a map using a GTK+ file picker widget
+
+	* GUI for robot statistics updated to use a normal GTK+ widget.
+	Robot statistics show deltas using color to highlight up/down
+	changes in the quantity.
+
+	* GUI: Delay is now done in the api.c file rather than in grobot.c.
+
+	* GUI: Cairo is now being used for the arena drawing (the old PixMap
+	method is deprecated)
+
+	* GUI: Map filename is now being displayed in the GUI
+
+	* GUI: Animation is now done using timers (no more sleeping on the GUI
+	thread).
+
+	* Scheme/GUI: added a menu option to allow the user to interrupt
+	scheme code.
+
+	* Scheme: added a new function robot-set-move-delay! to turn
+	on/off the move delay.
+
+	* Scheme: added getters for base action costs
+	(e.g. robot-get-move-energy-cost).
+
+	* Scheme: added getters for robot statistics
+	(e.g. robot-get-energy).
+
+	* Map Files: maps can now specify one or more locations where they want
+	the robot to spawn (it doesn't always have to be (1, 1)) by
+	putting an R in the location.
+
+	* Map: maps can now support orientation for any thing (sets the
+	stage for moving baddies, bullets, missiles, whatever).
+
+	* Documentation: added texi documentation (can build .info files,
+	.html files, ...), including adding Invoking gnurobots, function
+	and concept indices, ...
+
+	* Documentation: added doxygen-style documentation to grobot.c,
+	grobot.h and map.h
+
 2008-08-03  Bradley Smith  <brad@brad-smith.co.uk>
 
 	Adjust build system.
diff --git a/configure.ac b/configure.ac
index 01b7486..5cf3eb3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@
 ## along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
 
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT([GNU Robots], [1.2.0], [gnurobots@gnu.org], [gnurobots])
+AC_INIT([GNU Robots], [1.3.0], [gnurobots@gnu.org], [gnurobots])
 AC_PREREQ([2.59])
 AM_CONFIG_HEADER([config.h])
 AC_CONFIG_SRCDIR([include/api.h])
@@ -26,12 +26,16 @@ AM_INIT_AUTOMAKE
 dnl Checks for programs.
 AC_PROG_CC_C99
 
+dnl Check for Guile.  Prefer Guile 2.2 but use 2.0 if available.
+GUILE_PKG([2.2], [2.0])
 GUILE_FLAGS
 
-PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 2.35.1)
+dnl GLIB and GTHREAD
+PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 2.38)
 PKG_CHECK_MODULES(GTHREAD2, gthread-2.0 >= 2.4)
-PKG_CHECK_MODULES(GTK2, gtk+-2.0 >= 2.4)
-PKG_CHECK_MODULES(VTE, vte)
+
+AM_PATH_GTK_3_0
+PKG_CHECK_MODULES(VTE, [vte-2.91])
 
 schemedir="\$(pkgdatadir)/scheme"
 AC_SUBST(schemedir)
@@ -51,12 +55,6 @@ You need openpty to build this program.
 
 ]))
 
-AC_CHECK_LIB(guile-2.0, scm_to_locale_string, [], AC_MSG_ERROR([
-
-You need guile 2.0 to build this program.
-
-]))
-
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
 
diff --git a/contrib/mapedit.c b/contrib/mapedit.c
index d694941..9af2d18 100644
--- a/contrib/mapedit.c
+++ b/contrib/mapedit.c
@@ -819,7 +819,7 @@ message_box (char *title, char *text)
 {
   GtkWidget *label, *window, *button, *box;
 
-  window = gtk_window_new (GTK_WINDOW_DIALOG);	/* make window */
+  window = gtk_window_new (GDK_WINDOW_DIALOG);	/* make window */
 
   gtk_window_set_title (GTK_WINDOW (window), title);
 
diff --git a/doc/Makefile.am b/doc/Makefile.am
index ec000f9..4496cf5 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -18,3 +18,5 @@
 EXTRA_DIST =\
  contrib\
  Robots-HOWTO
+
+info_TEXINFOS = gnurobots.texi
diff --git a/doc/Robots-HOWTO b/doc/Robots-HOWTO
index 2920842..98ace5f 100644
--- a/doc/Robots-HOWTO
+++ b/doc/Robots-HOWTO
@@ -190,6 +190,9 @@ print nothing.
 			       something (a baddie, or even a prize or
 			       food) then the function returns a true
 			       (`#t') value.
+---------------------------------------------------------------------
+ robot-is-alive?       0       Returns #t if the robot is able to take
+                               (i.e. both energy and shields are > 0).
 ---------------------------------------------------------------------
  robot-stop            0       Exits GNU Robots immediately.
 ---------------------------------------------------------------------
@@ -201,6 +204,14 @@ print nothing.
 ---------------------------------------------------------------------
  robot-get-score       0       Returns the robot's score (how many
 			       prizes have been picked up.)
+---------------------------------------------------------------------
+ robot-set-move-delay! 0       Sets whether the system will delay 
+			       after the robot takes an action.  This
+			       is useful for keeping track of robot
+			       activity on the GUI.
+			       Defaults to #t (has delay).
+			       Set to #f to turn off the delay (i.e.
+			       to speed the robot up).
 =====================================================================
 
 
diff --git a/doc/gnurobots.texi b/doc/gnurobots.texi
new file mode 100644
index 0000000..745e9ef
--- /dev/null
+++ b/doc/gnurobots.texi
@@ -0,0 +1,626 @@
+\input texinfo
+@setfilename gnurobots.info
+@settitle GNU Robots
+
+@dircategory Games
+@direntry
+* GNU Robots: (gnurobots). A game/diversion where you construct a program for a little robot, then set him loose and watch him explore a world on his
+own.
+@end direntry
+
+@ifnottex
+@node Top
+@top GNU Robots Top
+
+GNU Robots is a game/diversion where you construct a program for a
+little robot, then set him loose and watch him explore a world on his
+own.
+
+@end ifnottex
+
+@menu
+* Overview::    Overview of GNU Robots
+* Invoking gnurobots:: Running gnurobots
+* Getting Started:: Programming your first commands
+* Robot Actions:: API Commands to Control the Robot.
+* Challenge Maps:: Maps to challenge your robot on.
+* Sample Robots:: Robots others have written for you to use as inspiration.
+* Function and Variable Indices:: Functions to control the Robot.
+* Concept Index:: Concept index.
+@end menu
+
+@node Overview
+@chapter Overview
+
+Hi, and welcome to GNU Robots!
+
+GNU Robots is a game/diversion where you construct a program for a
+little robot, then set him loose and watch him explore a world on his
+own.  The robot program is written in Scheme, and is implemented using
+GNU Guile.
+
+GNU Robots is an ideal way to introduce someone to the fundamentals of
+computer programming.  Scheme is a very simple language to learn, and
+by writing Scheme programs for GNU Robots, you can immediately enjoy
+the results of your work.
+
+That said, I'll disclose now that I am not, by nature, a Scheme
+programmer. I only picked up a bit of Scheme when I created GNU
+Robots, and I found that the GNU project had created this wonderful
+extension library that is GNU Guile.  In order to use my own program,
+I had to learn a bit of Scheme.  It wasn't hard.  Did I mention that
+Scheme is a simple language to learn?
+
+@cindex web site
+For more information, visit @url{http://www.gnu.org/software/gnurobots/}.
+
+@section Bugs
+@cindex bugs, reporting
+@cindex reporting bugs
+
+Bugs can be filed by sending email to @email{gnurobots@@gnu.org}.
+
+@node Invoking gnurobots
+@chapter Invoking gnurobots
+@cindex command, gnurobots
+@cindex gnurobots
+@cindex invoking gnurobots
+@cindex gnurobots, invoking
+@cindex command-line
+
+GNU Robots is invoked by calling the @command{gnurobots} command.
+
+The syntax for the @command{gnurobots} command is:
+
+@example
+gnurobots [@var{options}] [@var{scheme-file}]
+@end example
+
+For most usage, it is sufficient to run @command{gnurobots} with no arguments:
+
+@example
+gnurobots
+@end example
+
+@section Scheme-file
+@cindex command-line
+@cindex scheme-file (command line)
+@cindex scheme
+
+A scheme file to load can be provided on the command line.  This is
+optional---if a scheme file is not provided at the command line,
+another can be loaded using the REPL.
+
+@example
+gnurobots simple.scm
+@end example
+
+@cindex environment variable GNU_ROBOTS_SCHEME_PATH
+@cindex GNU_ROBOTS_SCHEME_PATH environment variable
+If the given @var{FILE} is not found, @command{gnurobots} will search
+based on the contents of the environment variable
+@env{GNU_ROBOTS_SCHEME_PATH}, then in its default directory
+(determined during compile, but something like
+@file{/usr/local/share/gnurobots/scheme}).
+
+@section Options
+@cindex options
+@cindex command-line options
+
+@table @option
+@item -f @var{FILE}
+@item --map-file=@var{FILE}
+@cindex -f option
+@cindex --map_file option
+@cindex option, -f
+@cindex option, --map_file
+Optional
+
+Specify a map file to load.  By default, @command{gnurobots} will try
+to load a default map.  You can also load a map using the GUI once the
+window is open.
+
+@cindex environment variable GNU_ROBOTS_MAPS_PATH
+@cindex environment variable GNU_ROBOTS_MAPS_PATH
+If the given @var{FILE} is not found, @command{gnurobots} will search
+based on the contents of the environment variable
+@env{GNU_ROBOTS_MAPS_PATH}, then in its default directory (determined
+during compile, but something like
+@file{/usr/local/share/gnurobots/maps}).
+
+@item -s @var{SHIELDS}
+@item --shields=@var{SHIELDS}
+@cindex -s option
+@cindex --shields option
+@cindex option, -s
+@cindex option, --shields
+
+Sets the robot's initial shields to the provided value @var{SHIELDS}.
+
+@item -e @var{ENERGY}
+@item --energy=@var{ENERGY}
+@cindex -e option
+@cindex --energy option
+@cindex option, -e
+@cindex option, --energy
+
+Sets the robot's initial energy to the provided value @var{ENERGY}.
+
+@item -V
+@item --version
+@cindex -V option
+@cindex --version option
+@cindex option, -V
+@cindex option, --version
+
+Display version information and exit.
+
+@item -h
+@item --help
+@cindex -h option
+@cindex --help option
+@cindex option, -h
+@cindex option, --help
+
+Display basic command line help and exit.
+
+@end table
+
+@node Getting Started
+@chapter Getting Started
+@cindex Getting Started
+
+Once GNU Robots is launched, the window shows a map in the top pane
+and a @dfn{scheme REPL} on the bottom of the screen.
+
+The map shows the world the robot is moving around in and the scheme
+REPL is where you enter Scheme programming language commands to
+control the robot.
+
+So the basic steps are:
+
+@enumerate
+@item
+Load a map
+@item
+Enter commands to control the robot
+@end enumerate
+
+@section The Objective
+
+The point of GNU Robots is to move your robot around the map
+collecting prizes and food while avoiding (or zapping) baddies.
+
+The challenge is to program a robot to be as independent as possible.
+
+@node Your First Map
+
+If GNU Robots loaded a map on start-up, you'll see a map and a file
+name on the top two thirds of the screen.  If this map is suitable for
+you, there's nothing more you need to do.
+
+@section Load a Map
+@cindex load map
+@cindex map, load
+
+You can load a new map using the menus (File/Open Map).
+
+@cindex sample maps
+@cindex map, samples
+There are sample maps shipped with GNU Robots.  The sample maps can
+usually be found in @file{/usr/local/share/gnurobots/maps}, though
+this can vary depending on your installation.
+
+Note that you may not want to load a new map after your robot has
+started moving around the map.  If your robot is keeping track of its
+position internally, loading a new map will confuse it greatly.
+
+If your robot does not keep track of what it has explored internally
+or you are manually moving the robot from the REPL, you're free to
+load a new map any time.
+
+@node Controlling the Robot
+@section Controlling the Robot
+@cindex scheme
+@cindex programming
+
+Gosh, what's the best way to introduce computer programming with GNU
+Robots?  I guess I'm not the kind of person who can teach someone else
+to do computer programming.  So I'll take the easy way out:
+
+Most of writing programs for GNU Robots involves writing Scheme code.
+Remember, your robot program is just a Scheme program.  So go buy
+yourself a nice introduction to Scheme book, if you don't already have
+one.  Or, if you're a particularly brave person, see what you can pick
+up from the robot examples under the @file{.../scheme/} directory.
+
+Scheme is not a difficult language to pick up, both for programmers
+and for those new to programming.  I would recommend that you adopt a
+@dfn{function} approach to programming a GNU Robot.  Many people will
+refer to this as a @dfn{divide and conquer} strategy, and I find it
+works well.
+
+@node Programming Your First Robot
+@subsection Programming Your First Robot
+@cindex scheme
+
+What this means is that you will want to identify the basic kinds of
+tasks that a robot can perform, then group those into particular kinds
+of activities.  For example, one basic activity for a robot might be
+to pick up something directly in front of it, then move into the space
+that is now empty:
+
+@lisp
+(define (grab-and-move)
+  (robot-grab)
+  (robot-move 1))
+@end lisp 
+
+The @code{define} statement is from Scheme, and allows us to define a
+function called @code{grab-and-move}.  The function consists of two
+statements: @code{robot-grab} to pick up a thing immediately in front of
+the robot, and @code{robot-move} to move the robot a given number of
+spaces.
+
+That was easy.  Now, let's try another.
+
+@node Improving Your Robot
+@subsection Improving Your Robot
+@cindex scheme
+
+The whole goal of GNU Robots is to get the highest score that you can.
+To increase your score, you need to pick up prizes.  So let's create a
+function that allows our little robot to pick up some prizes.  We'll
+build on the @code{grab-and-move} function that we defined above.
+
+@lisp
+(define (grab-prize)
+  (if (eqv? (robot-feel "prize") #t) (grab-and-move)))
+@end lisp
+
+This is not so scary a statement, if we look at it a piece at a time.
+The @code{eqv?} statement compares the truth value of two expressions.  In
+this case, we compare a test expression against a literal true value
+(@code{#t}).  Our test expression is @code{(robot-feel "prize")} which will
+return a @code{#t} value if the little robot is able to detect a prize in
+the space immediately in front of it.
+
+If the @code{eqv?} statement is successful (the two expression are the
+same, or in other words the robot is able to feel a prize in the space
+right in front of it) then Scheme will execute the @code{grab-and-move}
+function.
+
+In plain English: if the robot detects a prize in the space
+immediately before it, the robot will pick it up, and move forward
+into that space.
+
+But every time you make the little robot take an action, you cause it
+to use energy.  To restore its energy levels, it needs to find and
+consume food.  So let's create a function similar to the above that
+picks up a food item:
+
+@lisp
+(define (grab-food)
+  (if (eqv? (robot-feel "food") #t) (grab-and-move)))
+@end lisp
+
+As you can see, the @code{grab-food} function is virtually identical to the
+@code{grab-prize} function.  I don't think you need me to explain the
+difference.
+
+From here, you are on your way to creating a cool robot that you can
+set loose in GNU Robots.  It's just a matter of picking up some more
+of the flow control structures like do-while loops and more of the
+if-then tests.  Then you'll have a fine robot program to play with.
+
+@node Loading Scheme Files
+@section Loading Scheme Files
+@cindex scheme, loading
+@cindex loading scheme
+
+As your robot commands become more and more sophisticated, you will
+want to save them to one or more files so you can load them.
+
+Scheme files can be loaded in the scheme REPL using the load command.
+To load the sample scheme file simple.scm, you might enter a command
+like this at the REPL@footnote{The actual file location on your system
+may be different.}:
+
+@lisp
+(load "/usr/local/share/gnurobots/scheme/simple.scm")
+@end lisp
+
+Many people work most effectively on complex scripts by editing the
+script in another editor (like Emacs, for instance), then loading
+their completed scheme files into GNU Robots for execution.
+
+@node Robot Actions
+@chapter Robot Actions
+@cindex api
+@cindex scheme
+@cindex actions, robot
+
+But the question you're probably waiting to have answered is: "What
+can I make the little robot do?"  Ah, that's the list of @samp{robot-*}
+functions.
+
+Here is a table of all the GNU Robots functions, called primitives,
+that you can use in your robot program.  Each primitive has an energy
+cost associated with it.
+
+Note that some primitives will take an argument, and some will not.
+If an argument is a number, I'll print @var{N} after the function
+name.  If the argument is a GNU Robots "thing" then I'll print
+@var{TH} after the function name (@pxref{Things in the Map}).  If the
+primitive does not take an argument, I will print nothing.
+
+@deffn Function robot-turn N
+Energy cost: 2
+
+Turns the robot @var{N} spaces to the right or left.  If @var{N} is a
+positive number, the robot turns to the right.  If @var{N} is
+negative, the robot turns to the left.  Every turn has a cost of -2,
+so if you @code{(robot-turn 2)} then the cost is -4 to your energy.
+@end deffn
+
+@deffn Function robot-move N
+Energy cost: 2
+
+Moves the robot @var{N} spaces forwards or backwards.  If @var{N} is a
+positive number, the robot moves forwards.  If @var{N} is negative,
+the robot moves backwards.  Every move has a cost of -2, so if you
+@code{(robot-move 2)} then the cost is -4 to your energy.
+@end deffn
+
+@deffn Function robot-smell TH
+Energy cost: 1
+
+Smells for a thing @var{TH} (@pxref{Things in the Map}).  If the thing
+is in the surrounding eight spaces, the function returns a true
+(@code{#t}) value.
+@end deffn
+
+@deffn Function robot-feel TH
+Energy cost: 1
+
+Feels for a thing @var{TH} (@pxref{Things in the Map}) in the space
+directly in front of the robot.  If the thing is detected in that
+space, the function returns a true (@code{#t}) value.  Note that you
+can feel a baddie, and not take any damage.
+
+@end deffn
+@deffn Function robot-look TH
+Energy cost: 1
+
+Looks ahead across empty spaces for a particular thing @var{TH}
+(@pxref{Things in the Map}).  If the thing is seen, the function
+return returns a true (@code{#t}) value.  Note that this is sort of a
+brain-dead function, in that it does not provide the distance to the
+thing.  The robot cannot look behind objects, so if you look for a
+food item, and there is a prize item in the way, the function returns
+false.
+@end deffn
+
+@deffn Function robot-grab
+Energy cost: 5
+
+Attempts to pick up what is in the space directly in front of the
+little robot.  Note that you should not try to pick up baddies or
+walls.  If the item is food, you increase your energy by +10.  If the
+item is a prize, you increase your score by +1.  The function returns
+a true (@code{#t}) if the robot was able to pick the thing up.
+@end deffn
+
+@deffn Function robot-zap
+Energy cost: 10
+
+Uses the robot's little gun to zap whatever is in the space directly
+in front of it.  If you were able to zap something (a baddie, or even
+a prize or food) then the function returns a true (@code{#t}) value.
+@end deffn
+
+@section Robot Information
+
+The following functions return information about the robot, but do not
+cause the robot to take action.  The robot will not spend energy on
+these.
+
+@deffn Function robot-is-alive?
+Energy cost: 0
+
+Returns @code{#t} if the robot is able to take action (i.e. both
+energy and shields are > 0).
+@end deffn
+
+@deffn Function robot-stop
+Energy cost: 0
+
+Exits GNU Robots immediately.
+@end deffn
+
+@deffn Function robot-get-shields
+Energy cost: 0
+
+Returns the level of the little robot's shields.
+@end deffn
+
+@deffn Function robot-get-energy
+Energy cost: 0
+
+Returns the level of the little robot's energy.
+@end deffn
+
+@deffn Function robot-get-score
+Energy cost: 0
+
+Returns the robot's score (how many prizes have been picked up.)
+@end deffn
+
+@section Miscellaneous
+
+@deffn Function robot-set-move-delay! t-or-f
+Energy cost: 0
+
+Sets whether the system will delay after the robot takes an action.
+This is useful for keeping track of robot activity on the GUI.
+Defaults to @code{#t} (has delay).  Set to @code{#f} to turn off the
+delay (i.e.  to speed the robot up).
+
+The main reason for doing this is to quickly run a program.
+
+Activating this setting may put the GUI map display out of synch with
+the scheme code (the animations will lag behind the Scheme program).
+@end deffn
+
+@node Things in the Map
+@section Things in the Map
+@cindex things
+@cindex api
+
+And what kinds of @dfn{things} are out there to be detected by
+@code{robot-smell}, @code{robot-feel} and @code{robot-look}?  Here is
+a list of all possible things in the GNU Robots world:
+
+@defvr Thing baddie
+A nasty little critter that is generally bad for your health.  Either
+leave these alone, or zap them.  Don't try to pick them up, or you
+will inflict damage to your shields.  Don't bump up against them--that
+doesn't feel too good, either.
+@end defvr
+@defvr Thing space
+@cindex space
+An empty space.  There's nothing interesting here.
+@end defvr
+@defvr Thing food
+Yum!  A health item that will help to restore +10 points of your
+robot's energy levels.
+@end defvr
+@defvr Thing  prize
+Pick these up!  This will add +1 point to your score.  Remember, the
+goal of GNU Robots is to get the highest score!
+@end defvr
+@defvr Thing wall
+An obstacle.  You can't zap these, so you better go around them.
+Trying to grab a wall does nothing to you, but bumping up against one
+will inflict damage to your shields.
+@end defvr
+
+@node Challenge Maps
+@chapter Challenge Maps
+@cindex maps, challenge
+@cindex challenge maps
+
+Several challenge maps are available to test your robots on. Load up
+the map, start your robot and see how well it does.
+
+For information on loading maps, see @xref{Getting Started}.
+
+@table @file
+@item challenge_tiny.map
+Difficulty: easy
+
+A tiny map giving the robot a chance to encouter a baddie, some food
+and a prize.  Extra credit for robots that realize they have explored
+the whole area and stop.
+
+@item challenge_backtrack.map
+Difficulty: medium
+
+In this map most robots will explore one side before the other and
+will have to realize and backtrack.
+
+@item challenge_blocked.map
+Difficulty: medium
+
+This map has a prize being blocked by some baddies.  The robot will
+have to zap a baddie to get behind it.  The best robots will identify
+that it can explore the whole area with only zapping one of the
+baddies.
+
+@item challenge_baddies.map
+Difficulty: hard
+
+There are enough baddies on this map to break the robot.  The best
+robots will identify the baddie once and will not touch or drive into
+it again.  Energy and shield efficiency are very important on this
+map.
+
+@end table
+
+@node Sample Robots
+@chapter Sample Robots
+@cindex sample robots
+@cindex robots, sample
+@cindex scheme
+
+There are a number of robots included in GNU Robots that can help you
+get started.
+
+The best robot to look at just to get started is @file{simple.scm}.
+
+When you want to get really sophisticated in your robot's
+capabilities, take a look at @file{greedy.scm}.  When you're ready to
+get fancy, have a look at @file{mapper.scm} and @file{bot-wander.scm}.
+
+@ref{Getting Started} for information on how to load a scheme file.
+
+@table @file
+
+@item simple.scm
+An excellent robot to start with.  This robot is meant to be used with
+@file{simple.map}.  It has been hand-written to collect the prizes on
+the outside of that map (i.e. this robot is built already knowing
+about the layout of the map).  It's a great one to start with for the
+basics of moving and grabbing.
+
+@item greedy.scm
+This robot wanders around looking for prizes and food and collects
+them.  This is an excellent robot to look at when you're starting to
+think about how to move and collect items independently.
+
+@item mapper.scm
+This robot uses well thought out logic to explore the map and collect
+prizes.
+
+This is a good map to look at for inspiration on getting really fancy.
+
+@item bot-wander.scm
+This robot can do well on all the challenge maps.  It explores
+independently, zaps baddies if it has to (but not if it has better
+options) and will thoroughly explore any map until it either has
+explored everythig or it runs out of energy.
+
+When it completes, this robot prints out a representation of the map
+according to its own exploration.
+
+This is a good map to look at for inspiration on getting really fancy.
+
+@item beep.scm
+Very simple robot that beeps when it finds a prize next to it.
+
+@item stop.scm
+Not much to see here.  A robot that was used to test the quit and stop
+commands.
+
+@item zap.scm
+This robot turns in place and zaps whatever it finds.
+
+@end table
+
+@node Function and Variable Indices
+@unnumbered Function and Variable Indices
+
+@section Function Index
+
+@printindex fn
+
+@section Variable Index
+
+@printindex vr
+
+@node Concept Index
+@unnumbered Concept Index
+
+@printindex cp
+
+@bye
diff --git a/include/Makefile.am b/include/Makefile.am
index 75e1b2f..fcdb5b3 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -24,4 +24,5 @@ EXTRA_DIST = \
  ui-window.h \
  ui-window-private.h \
  ui-arena.h \
- ui-cmdwin.h
+ ui-cmdwin.h \
+ ui-status.h
diff --git a/include/api.h b/include/api.h
index eb58783..ae10e7d 100644
--- a/include/api.h
+++ b/include/api.h
@@ -20,23 +20,27 @@
 #ifndef _API_H
 #define _API_H
 
-#include <glib.h>
-#include <libguile.h>           /* GNU Guile high */
-
-void		api_init			(void);
-
-/* Functions */
-SCM		api_robot_turn 			(SCM s_n);
-SCM 		api_robot_move 			(SCM s_n);
-SCM 		api_robot_smell 		(SCM s_th);
-SCM 		api_robot_feel 			(SCM s_th);
-SCM 		api_robot_look 			(SCM s_th);
-SCM 		api_robot_grab 			(void);
-SCM 		api_robot_zap 			(void);
-SCM 		api_robot_stop 			(void);
+/** Send an interrupt to Guile causing it to stop processing and drop
+ *  to the debugger (if it's in an REPL call anyway).
+ *
+ * This is accomplished by generating the SIGUSR1 signal.
+ */
+void            api_interrupt   (void);
 
-SCM 		api_robot_get_shields 		(void);
-SCM 		api_robot_get_energy 		(void);
-SCM 		api_robot_get_score 		(void);
+/** Initialize the Guile environment in a newly created thread.
+ *
+ * Note: pass unprocessed program arguments (argc and argv) so that
+ * Guile can appropriately set its (command-line) data.
+ *  
+ * When Guile exits, it will call exit() on the program (terminating
+ * it).
+ *
+ * \param[in] argc The number of arguments Guile can processin in argv.
+ * \param[in] argv The arguments as strings.
+ * \param[in, out] in_robot the robot the API functions are meant to control.
+ *
+ * \returns a pointer to the GThread created.
+ */
+GThread*        api_init        (gint argc, gchar **argv, gpointer in_robot);
 
 #endif /* _API_H */
diff --git a/include/grobot.h b/include/grobot.h
index 60796a9..1c32ac3 100644
--- a/include/grobot.h
+++ b/include/grobot.h
@@ -21,74 +21,331 @@
 
 #include <glib-object.h>
 #include <glib.h>
-#include "ui-arena.h"
 #include "map.h"
 
 G_BEGIN_DECLS
 
+/** Normal GObject function to get the type (makes G_TYPE_ROBOT work,
+ * for instance).
+ */
+GType g_robot_get_type(void) G_GNUC_CONST;
+
 #define G_TYPE_ROBOT            g_robot_get_type()
+/** Use to test whether you have a valid GRobot*.
+ */
 #define G_IS_ROBOT(obj)         G_TYPE_CHECK_INSTANCE_TYPE ((obj),\
                                   G_TYPE_ROBOT)
 #define G_IS_ROBOT_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE ((klass),\
                                   G_TYPE_ROBOT)
 #define G_ROBOT_GET_CLASS(obj)  G_TYPE_INSTANCE_GET_CLASS ((obj),\
                                   G_TYPE_ROBOT, GRobotClass)
+/** Use to cast to GRobot*.
+ */
 #define G_ROBOT(obj)            G_TYPE_CHECK_INSTANCE_CAST ((obj),\
                                   G_TYPE_ROBOT, GRobot)
 #define G_ROBOT_CLASS(klass)    G_TYPE_CHECK_CLASS_CAST ((klass),\
                                   G_TYPE_ROBOT, GRobotClass)
 
+/** Main type for robots in GNU Robots.
+ */
 typedef struct _GRobot GRobot;
 typedef struct _GRobotClass GRobotClass;
+typedef struct _GRobotPrivate GRobotPrivate;
+
+/** GRobot_stats is used to communicate the statistics of the robot at
+ * a point in time.
+ */
+typedef struct _GRobot_stats GRobot_stats;
+/** GRobot_change is used to communicate the statistics of the robot at
+ * across a change.  
+ *
+ * It includes both before- and after-change GRobot_stats so the
+ * consumer can calcaulate deltas.  
+ * It also includes a status_message indicating what caused the change
+ * (e.g. robot-move will be passed if the robot changes are the result
+ * of a move).
+ */
+typedef struct _GRobot_change GRobot_change;
 
+/** _GRobot_stats is used to communicate the statistics of the robot at
+ * a point in time.
+ */
+struct _GRobot_stats {
+  gint      x;       /*!< The robot's x-position. */
+  gint      y;       /*!< The robot's y-position. */
+  gint      dir;     /*!< The direction the robot is facing as defined in configs.h (NORTH, SOUTH, EAST, WEST). */
+  glong     turn;    /*!< Counter of the number of robot-actions the robot has taken. */
+  glong     score;   /*!< The robot's score. Score increases as the robot grabs prizes. */
+  glong     energy;  /*!< The robot's current energy. Energy is decreased when the robot takes actions. */
+  glong     shields; /*!< The robot's current shields.  Shields are depleted when the robot touches baddies or when the robot bumps into baddies or into walls. */
+  glong     shots;   /*!< The number of shots the robot has taken. */
+  glong     units;   /*!< Count of the number of times the robot has moved. */
+};
+
+/** _GRobot is used to hold the robot's instance data.
+ */
 struct _GRobot {
-  GObject   object;
-
-  gint      x;
-  gint      y;
-  gint      dir;
-  glong     score;
-  glong     energy;
-  glong     shields;
-  glong     shots;
-  glong     units;
-
-  UIArena *ui;
-  Map *map;
+  GObject   object;    /*!< Pointer to the robot's parent instance. */
+
+  GRobotPrivate *priv; /*!< Pointer to the robot's private data. */
 };
 
+/** _GRobotClass is used to hold the robot's class data.
+ */
 struct _GRobotClass {
-  GObjectClass parent_class;
+  GObjectClass parent_class; /*!< Pointer to the robot's parent class. */
 
   void (*death) (GRobot *robot);
 };
 
-/* some convenient macros */
-#define G_ROBOT_POSITION_X(robot)   ((robot)->x)
-#define G_ROBOT_POSITION_Y(robot)   ((robot)->y)
+/** Max length for a _GRobot_change.status_message.
+ */
+#define ROBOT_STATUS_MESSAGE_LEN 20
 
-/* this is a little weird, but 0 case doesn't actually matter
-   given how the code in grobot.c uses this macro... */
-#define sign(x)                     (x ? x/abs(x) : 0)
+/** _GRobot_change is produced by the "robot-stats" signal containing
+ * data describing the robot's stats before and after the change.
+ */
+struct _GRobot_change {
+  gchar status_message[ROBOT_STATUS_MESSAGE_LEN]; /*!< Text description describing the source of the change (e.g. "robot-move", "robot-turn", ...). */
+  GRobot_stats before_change; /*!< Stats of the robot before the change. */
+  GRobot_stats after_change;  /*!< Stats of the robot after the change. */
+};
 
-/* normal GObject stuff */
-GType g_robot_get_type(void) G_GNUC_CONST;
+/** \defgroup RConstructor Robot Constructor
+ * @{
+ */
 
-/* Our object functions */
-GRobot* g_robot_new(int x, int y, int dir, long score, long energy,
-        long shield, long units, long shots, UIArena *ui, Map *map);
+/** Creates a new GRobot.
+ *
+ * \param[in] energy is the robot's starting energy (should be a number > 0).  DEFAULT_ENERGY is a typical starting setting.
+ * \param[in] shield is the robot's starting shields (should be a number > 0).  DEFAULT_SHIELDS is a typical starting setting.
+ * \param[in] units is the starting value for units (typically 0).
+ * \param[in] shots is the starting value for shots (typically 0).
+ * \param[in] map is the map the robot will be moving around on.
+ *
+ * \returns a new GRobot*.
+ */
+GRobot* g_robot_new(long energy, long shield, long units, long shots,
+		    Map *map);
+
+/** @} */
+
+/** \defgroup RActions Robot Actions
+ * @{
+ *
+ * Robot actions are how the robot is moved around and takes action in
+ * GNU Robots.  Robot Actions consume energy.
+ */
 
-void        g_robot_turn            (GRobot *robot, gint num_turns);
+/** Turn the robot.
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot to turn.
+ * \param[in] num_turns the number of quarter turns to take.  Positive
+ * numbers turn clockwise; negative numbers turn counter-clockwise.
+ *
+ * \returns TRUE if the turn succeeds.  Note that it is possible for a
+ * turn to partially succeed (e.g. robot runs out of energy part way
+ * through a 5 num_turns turn).  In a partial move scenario, it returns
+ * FALSE, but the robot may have rotated from its original position.
+ */
+gboolean    g_robot_turn            (GRobot *robot, gint num_turns);
+/** Move the robot.
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot to move.
+ * \param[in] steps the number of spaces to move.
+ *
+ * \returns TRUE if the move succeeds.  Note that it is possible for a
+ * move to partially succeed (e.g. robot runs out of energy part way
+ * through a 5 step move).  In a partial move scenario, it returns
+ * FALSE, but the robot may have moved from its original position.
+ */
 gboolean    g_robot_move            (GRobot *robot, gint steps);
+/** Smell the spaces around the robot (NORTH, SOUTH, EAST, WEST and
+ * NORTH-EAST, NORTH-WEST and SOUTH-EAST and SOUTH-WEST).
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot doing the smelling.
+ * \param[in] str a string representing the item being smelled for.  This
+ *      is one of the things[] items in grobot.c ("space", "food",
+ *      "prize", "wall", "baddie", "robot").
+ *
+ * \returns TRUE if the thing is smelled.
+ */
 gboolean    g_robot_smell           (GRobot *robot, gchar *str);
+/** Feel the location in front of the robot.
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot doing the feeling.
+ * \param[in] str a string representing the item being felt for.  This
+ *      is one of the things[] items in grobot.c ("space", "food",
+ *      "prize", "wall", "baddie", "robot").
+ *
+ * \returns TRUE if the thing is felt.
+ */
 gboolean    g_robot_feel            (GRobot *robot, gchar *str);
+/** Look at the locations in front of the robot.
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot doing the looking.
+ * \param[in] str a string representing the item being looked for.  This
+ *      is one of the things[] items in grobot.c ("space", "food",
+ *      "prize", "wall", "baddie", "robot").
+ *
+ * \returns TRUE if the thing is seen.
+ */
 gboolean    g_robot_look            (GRobot *robot, gchar *str);
+/** Grab the item in the location in front of the robot.
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot doing the grabbing.
+ *
+ * \returns TRUE if the grab is successful.  FALSE if unsuccessful
+ * (e.g. no item, item is wall, item can't be grabbed).
+ */
 gboolean    g_robot_grab            (GRobot *robot);
+/** Zap the location in front of the robot.
+ *
+ * This action consumes energy.
+ *
+ * \param[in, out] robot A pointer to the robot doing the zapping.
+ *
+ * \returns TRUE if the zap is successful. Success only means the
+ * robot performed the zap; there may not have been anything in the
+ * location that was zappable.
+ */
 gboolean    g_robot_zap             (GRobot *robot);
+/** Stop the robot.  
+ *
+ * Usually indicates that the robot is done taking actions.  No more
+ * actions will succeed after the robot stops.
+ *
+ * \param[in, out] robot A pointer to the robot doing the stopping.
+ *
+ * \returns TRUE
+ */
 gboolean    g_robot_stop            (GRobot *robot);
+
+/** @} */
+
+/** \defgroup RStats Robot Statistics
+ * @{
+ *
+ * The statistics functions give infromation about the robot's current
+ * statistics, e.g. energy, shields, score...
+ */
+
+/** Return the number of turns (actions) the robot has performed.
+ *
+ * \param[in] robot A pointer to the robot.
+ *
+ * \returns the number of turns (actions) the robot has performed.
+ */
+glong       g_robot_get_turn        (GRobot *robot);
+/** Return the amount of shields the robot has.
+ *
+ * \param[in] robot A pointer to the robot.
+ *
+ * \returns the amount of shields robot has has.
+ */
 glong       g_robot_get_shields     (GRobot *robot);
+/** Return the amount of energy the robot has.
+ *
+ * \param[in] robot A pointer to the robot.
+ *
+ * \returns the amount of energy robot has has.
+ */
 glong       g_robot_get_energy      (GRobot *robot);
+/** Return the score the robot achieved.
+ *
+ * Score indicates the number of prizes the robot has collected.
+ *
+ * \param[in] robot A pointer to the robot.
+ *
+ * \returns the score robot has has achieved.
+ */
 glong       g_robot_get_score       (GRobot *robot);
+/** Return the amount of shots the robot has taken.
+ *
+ * \param[in] self A pointer to the robot.
+ *
+ * \returns the amount of shots the robot has taken.
+ */
+glong       g_robot_get_shots       (GRobot *self);
+
+/** @} */
+
+/** \defgroup RCosts Robot Action Energy Costs
+ * @{
+ *
+ * These functions return information on the normal costs of taking
+ * various Robot Actions.  They can be used by robot control programs
+ * to plan their energy budget.
+ */
+
+/** Get the energy cost for moving.
+ *
+ * \return the energy cost for moving.
+ */
+glong       g_robot_get_move_energy_cost();
+/** Get the energy cost for turning.
+ *
+ * \return the energy cost for turning.
+ */
+glong       g_robot_get_turn_energy_cost();
+/** Get the energy cost for feeling.
+ *
+ * \return the energy cost for feeling.
+ */
+glong       g_robot_get_feel_energy_cost();
+/** Get the energy cost for smelling.
+ *
+ * \return the energy cost for smelling.
+ */
+glong       g_robot_get_smell_energy_cost();
+/** Get the energy cost for looking.
+ *
+ * \return the energy cost for looking.
+ */
+glong       g_robot_get_look_energy_cost();
+/** Get the energy cost for grabbing.
+ *
+ * \return the energy cost for grabbing.
+ */
+glong       g_robot_get_grab_energy_cost();
+/** Get the energy cost for zapping.
+ *
+ * \return the energy cost for zapping.
+ */
+glong       g_robot_get_zap_energy_cost();
+
+/** @} */
+
+/**
+ * \defgroup RStatus Robot Status
+ * @{
+ */
+
+/** Determine if the robot is alive.
+ *
+ * The robot is alive it its energy and shields are both greater than
+ * zero and the robot has not been stopped.
+ * \sa g_robot_stop
+ *
+ * \param[in] self A pointer to the robot.
+ *
+ * \returns TRUE if the robot is alive; FALSE otherwise.
+ */
+gboolean    g_robot_is_alive        (GRobot *self);
+
+/** @} */
 
 G_END_DECLS
 
diff --git a/include/main.h b/include/main.h
deleted file mode 100644
index c93751b..0000000
--- a/include/main.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (C) 2008 Bradley Smith <brad@brad-smith.co.uk>
- *
- * GNU Robots Game.
- *
- * GNU Robots is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GNU Robots is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <grobot.h>
-
-void main_prog (void *closure, gint argc, gchar *argv[]);
-void death (GRobot *robot);
-void exit_nicely (void);
-void usage (const gchar *argv0);
-gint is_file (const gchar *filename);
diff --git a/include/map.h b/include/map.h
index 64389bb..8dc4bd9 100644
--- a/include/map.h
+++ b/include/map.h
@@ -24,49 +24,391 @@
 
 G_BEGIN_DECLS
 
+/** Normal GObject function to get the type (makes G_TYPE_MAP work,
+ * for instance).
+ */
+GType map_get_type(void) G_GNUC_CONST;
+
 #define G_TYPE_MAP			  map_get_type()
-#define G_IS_MAP(obj)		  G_TYPE_CHECK_INSTANCE_TYPE((obj), \
-                                  G_TYPE_MAP)
+
+/** Use to test whether you have a valid Map*.
+ */
+#define G_IS_MAP(obj)		  G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_MAP)
 #define G_IS_MAP_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_MAP)
 #define MAP_GET_CLASS(obj)	  G_TYPE_INSTANCE_GET_CLASS((obj), \
                                   G_TYPE_MAP, MapClass)
-#define MAP(obj)			  G_TYPE_CHECK_INSTANCE_CAST((obj), \
-                                  G_TYPE_MAP, Map)
+/** Use to cast to Map*.
+ */
+#define MAP(obj)		  G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_MAP, Map)
 #define MAP_CLASS(klass)	  G_TYPE_CHECK_CLASS_CAST((klass), \
                                   G_TYPE_MAP, MapClass)
 
-typedef struct
-{
-  gint num_rows;
-  gint num_cols;
-} MapSize;
-
+/** Main type for Maps in GNU Robots.
+ */
 typedef struct _Map Map;
 typedef struct _MapClass MapClass;
 
-struct _Map {
-  GObject 	object;
+typedef struct _MapLoc MapLoc;
+typedef struct _MapPrivate MapPrivate;
 
-  /* The actual Map */
-  gint 		**_map;
-  MapSize	size;
+struct _Map {
+  GObject 	object; /*!< the Map's parent instance */
 
-  gint 		errors;
+  MapPrivate    *priv; /*!< All values should be treated as private. */
 };
 
 struct _MapClass {
-  GObjectClass	parent_class;
+  GObjectClass	parent_class; /*!< the Map's parent class */
+};
+
+/** MapMove is used in the map-move signal to communicate information
+ *  about a move on the map.
+ */
+typedef struct _MapMove MapMove;
+
+/** MapMove is used in the map-move signal to communicate information
+ *  about a move on the map.
+ */
+struct _MapMove {
+  gchar thing;   /*!< The character reference describing the thing moved (usually it's the ROBOT). */
+  gint origin_x; /*!< Origin x-value for the move. */
+  gint origin_y; /*!< Origin y-value for the move. */
+  gint dest_x;   /*!< Destination x-value for the move. */
+  gint dest_y;   /*!< Destination y-value for the move. */
+};
+
+/** Struct to be used to pass around coordinates.
+ */
+typedef struct _MapCoords MapCoords;
+
+/** Struct to be used to pass around coordinates.
+ */
+struct _MapCoords {
+  gint x; /*!< x-coordinate */
+  gint y; /*!< y-coordinate */
 };
 
-/* some convenient macros */
-#define MAP_GET_OBJECT(map, x, y)	    	((map)->_map[(y)][(x)])
-#define MAP_SET_OBJECT(map, x, y, thing)	((map)->_map[(y)][(x)] = thing)
+/** \defgroup MConstructor Map Constructor
+ * @{
+ */
+
+/** Helper function to generate a new map.
+ *
+ * \param[in] max_x the maximum x value for the map.  max_x must be
+ *            positive.
+ * \param[in] max_y the maximum y value for the map.  max_y must be
+ *            positive.
+ *
+ * \returns A new *Map initialized to all walls.
+ */
+Map *    map_new_blank (gint max_x, gint max_y);
+
+/** @} */
+
+/** \defgroup MMassUpdate Map Mass Update Methods
+ * @{
+ */
+
+/** Initialize the map to all walls.
+ *
+ * Resets all the locations in self to be walls with no things in
+ * them.
+ *
+ * \param[in, out] self the map to update
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_init_map(Map *self);
+/** Load a new map from a file.
+ *
+ * Loads a new map from the given filename.  Uses the map's dimensions
+ * from when the map was constructed.
+ *
+ * Initially all locations are set to be walls, so the load will add
+ * itself into a map the size of map_get_max_x and map_get_max_y.
+ *
+ * This will emit a map-update signal.
+ *
+ * \param[in, out] self the map to update
+ * \param[in] filename the path and name of the file to load.  It is
+ *            safe to free this after the function completes.
+ * \param[out] error a GError object representing details about errors
+ *             encountered. error is set to NULL if the function is
+ *             successful.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_load_filename(Map *self, const gchar *filename, GError **error);
+
+/** @} */
+
+/** \defgroup MModify Map Update Methods
+ * @{
+ */
+
+/** Set Location as walls or spaces.
+ *
+ * This will emit a map-update signal.
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] x the x portion of the coordinate to update.
+ * \param[in] y the y portion of the coordinate to update.
+ * \param[in] is_wall supply TRUE to set the location to be a wall;
+ *            FALSE to set it to be a space.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_set_loc(Map *self, gint x, gint y, gboolean is_wall);
+
+/** Manipulating Things in Locations **/
+
+/** Remove the thing in the location (e.g. baddie, prize, food, ...).
+ *
+ * This will emit a map-update signal.
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] x the x portion of the coordinate to update.
+ * \param[in] y the y portion of the coordinate to update.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_remove_thing(Map *self, gint x, gint y);
+/** Place the thing in the location (e.g. baddie, prize, food, ...).
+ *
+ * This will emit a map-update signal.
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] x the x portion of the coordinate to update.
+ * \param[in] y the y portion of the coordinate to update.
+ * \param[in] thing_type thing's identifier as defined in configs.h
+ *            (e.g. ROBOT, BADDIE, PRIZE, FOOD).
+ * \param[in] orientation the direction the thing is facing (NORTH,
+ *            SOUTH, EAST, WEST as defined in configs.h).
+ * \param[in] thing usually supplied only for GRobots. Allows the
+ *            map_find_thing function to find the thing again even
+ *            after it has moved. For untracked things (baddies, food,
+ *            prizes), set this to NULL.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_place_thing(Map *self, gint x, gint y, gint thing_type,
+			 gint orientation, gpointer thing);
+/** Automatically place the thing on the map.
+ *
+ * Can be useful to automatically place objects like robots on maps
+ * that did not assign robot starting locations.
+ *
+ * This method will search the map from the top left to the bottom
+ * right looking for a location matching on_loc_thing_char on which to
+ * place thing_char.
+ *
+ * This will emit a map-update signal.
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] thing_char thing's identifier as defined in configs.h
+ *            (e.g. ROBOT, BADDIE, PRIZE, FOOD).
+ * \param[in] orientation the direction the thing is facing (NORTH,
+ *            SOUTH, EAST, WEST as defined in configs.h).
+ * \param[in] thing usually supplied only for GRobots. Allows the
+ *            map_find_thing function to find the thing again even
+ *            after it has moved. For untracked things (baddies, food,
+ *            prizes), set this to NULL.
+ * \param[in] on_loc_thing_char the location/thing's identifier for
+ *            locations eligible to hold the thing_char being placed.
+ *            Most often this will be SPACE.  Defined in configs.h
+ *            (e.g. SPACE, ROBOT, BADDIE, PRIZE, FOOD, WALL).
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_auto_place_thing_on(Map *self, gint thing_char,
+				 gint orientation, gpointer thing,
+				 gchar on_loc_thing_char);
+/** Automatically place the robot on the map.
+ *
+ * If the map assigned one or more starting locations for the robot,
+ * this will assign the robot to one of them from top to bottom,
+ * left to right (typewriter style).
+ *
+ * If no starting locations are available either because they have all
+ * been used or they were not assigned, calls map_auto_place_thing_on
+ * with on_loc_thing_char set to SPACE.
+ *
+ * If map_auto_place_thing_on fails, this will set location (1, 1) to be
+ * an empty space and will put the robot there.
+ *
+ * The robot will always be facing EAST.
+ *
+ * This will emit a map-update signal.
+ *
+ * \sa map_auto_place_thing_on
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] robot the robot to place (should not be NULL).
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_auto_place_robot(Map *self, gpointer robot);
+/** Rotate the thing in the location (e.g. robot, baddie, prize, food, ...).
+ *
+ * Normally only used for the robot, but is capable of tracking the
+ * orientation of any object on the map.
+ *
+ * This will emit a map-update signal.
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] x the x portion of the coordinate to update.
+ * \param[in] y the y portion of the coordinate to update.
+ * \param[in] num_turns the number of quarter turns to take.  Positive
+ *            numbers turn clockwise; negative numbers turn
+ *            counter-clockwise.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_thing_turn_clockwise(Map *self, gint x, gint y, gint num_turns);
+/** Move the thing in the location (e.g. robot, baddie, prize, food, ...).
+ *
+ * Normally only used for the robot, but is capable of tracking the
+ * movement of any object on the map.
+ *
+ * map_move does not require that the two locations be adjacent.
+ *
+ * This will emit a map-move signal (there will be no map-update
+ * signal emitted).
+ *
+ * \param[in, out] self the map to update.
+ * \param[in] origin_x the x portion of the origin coordinate to update.
+ * \param[in] origin_y the y portion of the origin coordinate to update.
+ * \param[in] dest_x the x portion of the destination coordinate to update.
+ * \param[in] dest_y the y portion of the destination coordinate to update.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_move(Map *self, gint origin_x, gint origin_y,
+		  gint dest_x, gint dest_y);
+/** @} */
+
+/** \defgroup MGet Map Inquiry Methods
+ * @{
+ */
+
+/** Get the maximum allowed value of x for the given map (self).
+ *
+ * \param[in] self the map to inquire on.
+ *
+ * \returns the maximum x-value allowed on the map (the minimum value
+ *          is always 0).
+ */
+gint     map_get_max_x(Map *self);
+/** Get the maximum allowed value of y for the given map (self).
+ *
+ * \param[in] self the map to inquire on.
+ *
+ * \returns the maximum y-value allowed on the map (the minimum value
+ *          is always 0).
+ */
+gint     map_get_max_y(Map *self);
+
+/** Determine whether location (x, y) is a wall.
+ *
+ * \param[in] self the map to inquire on.
+ * \param[in] x the x portion of the coordinate to inquire.
+ * \param[in] y the y portion of the coordinate to inquire.
+ *
+ * \returns TRUE if the location is a wall; FALSE otherwise.
+ */
+gboolean map_get_loc(Map *self, gint x, gint y);
+
+/** Determine the orientation of the thing at location (x, y).
+ *
+ * \param[in] self the map to inquire on.
+ * \param[in] x the x portion of the coordinate to inquire.
+ * \param[in] y the y portion of the coordinate to inquire.
+ *
+ * \returns NORTH, SOUTH, EAST or WEST (as defined in configs.h).
+ *          Returns -1 on failure.
+ */
+gint     map_get_thing_orientation(Map *self, gint x, gint y);
+/** Determine the loc/thing char of the thing at location (x, y).
+ *
+ * \param[in] self the map to inquire on.
+ * \param[in] x the x portion of the coordinate to inquire.
+ * \param[in] y the y portion of the coordinate to inquire.
+ *
+ * \returns SPACE, WALL, ROBOT, BADDIE, PRIZE, FOOD (as defined in
+ *          configs.h).  Returns -1 on failure.
+ */
+gchar    map_get_loc_thing_char(Map *self, gint x, gint y);
+/** Determine whether thing is on the map and return its map
+ * information if it is.
+ *
+ * Searches the map (self) for the pointer thing.  Returns the value
+ * associated with the first thing it finds (it's possible to add the
+ * same thing multiple times, though this is not recommended).
+ *
+ * The parameters x, y and orientation are optional.  Supply NULL if
+ * you are not interested in any of those parameters.
+ *
+ * \param[in] self the map to inquire on.
+ * \param[in] thing the thing to inquire on---thing should not be NULL.
+ * \param[out] x the x portion of the coordinate found.  Set to NULL
+ *             if you do not need this information.
+ * \param[out] y the y portion of the coordinate found.  Set to NULL
+ *             if you do not need this information.
+ * \param[out] orientation the orientation of the thing found (NORTH,
+ *             SOUTH, EAST, WEST as defined in configs.h).  Set to
+ *             NULL if you do not need this information.
+ *
+ * \returns TRUE if the thing is found; FALSE otherwise.
+ */
+gboolean map_find_thing(Map *self, gpointer thing,
+			gint* x, gint* y, gint* orientation);
+/** Return the filename that was used to load the map.
+ *
+ * \param[in] self the map to inquire on.
+ *
+ * \returns the file name loaded or NULL.  If a filename is returned,
+ *          the caller is responsible for calling g_free on it when it
+ *          does not need it any longer.
+ */
+gchar*   map_get_filename(Map *self);
+
+/** @} */
 
-/* normal GObject stuff */
-GType map_get_type(void);
+/** \defgroup MUtil Map Coordinate Utility Methods
+ * @{
+ *
+ * Helper functions for dealing with direction logic.
+ */
+
+/** Given an orientation and a number of turns, calculate the new
+ * orientation if those turns were performed.
+ *
+ * \param[in] orientation the orientation (NORTH, SOUTH, EAST, WEST as
+ *            defined in configs.h).
+ * \param[in] num_turns the number of quarter turns to take.  Positive
+ *            numbers turn clockwise; negative numbers turn
+ *            counter-clockwise.
+ *
+ * \returns orientation the orientation (NORTH, SOUTH, EAST, WEST as
+ *          defined in configs.h).
+ */
+gint map_compute_clockwise_turn(gint orientation, gint num_turns);
+/** Calculate the coordinates of the location one away in the
+ * direction given by orientation.
+ *
+ * \param[in] from_x the x portion of the origin coordinates.
+ * \param[in] from_y the y portion of the origin coordinates.
+ * \param[in] orientation the orientation (NORTH, SOUTH, EAST, WEST as
+ *            defined in configs.h).
+ * \param[out] facing_x the x portion of the facing coordinates.
+ * \param[out] facing_y the y portion of the facing coordinates.
+ *
+ * \returns TRUE if successful; FALSE otherwise.
+ */
+gboolean map_compute_facing_coords(gint from_x, gint from_y, gint orientation,
+				   gint* facing_x, gint* facing_y);
 
-/* Our object functions */
-Map* map_new_from_file(const gchar *map, gint num_rows, gint num_cols);
+/** @} */
 
 G_END_DECLS
 
diff --git a/include/ui-arena.h b/include/ui-arena.h
index efcb32c..25c9dd5 100644
--- a/include/ui-arena.h
+++ b/include/ui-arena.h
@@ -44,41 +44,27 @@ typedef struct _UIArenaPrivate UIArenaPrivate;
 
 struct _UIArena
 {
-  GtkDrawingArea widget;
+  GtkVBox widget;
   UIArenaPrivate* priv;
 };
 
 struct _UIArenaClass
 {
-  GtkDrawingAreaClass parent_class;
+  GtkVBoxClass parent_class;
 };
 
 GType ui_arena_get_type(void) G_GNUC_CONST;
 
-GtkWidget *ui_arena_new();
-
-void ui_arena_set_map(UIArena *arena, Map *map);
-void ui_arena_postinit(UIArena *arena);
-void ui_arena_draw(UIArena *arena);
-void ui_arena_update_status(UIArena *arena, const gchar *fmt, const gchar *thing,
-	glong energy, glong score, glong shields);
-
-void ui_arena_add_thing(UIArena *arena, gint x, gint y, gint thing);
-void ui_arena_move_robot(UIArena *arena, gint from_x, gint from_y,
-	gint to_x, gint to_y, gint cdir, glong energy, glong score,
-	glong shields);
-void ui_arena_robot_smell(UIArena *arena, gint x, gint y, gint cdir,
-	glong energy, glong score, glong shields, const gchar *thing);
-void ui_arena_robot_zap(UIArena *arena, gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields);
-void ui_arena_robot_feel(UIArena *arena, gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields,
-	const gchar *thing);
-void ui_arena_robot_grab(UIArena *arena, gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields);
-void ui_arena_robot_look(UIArena *arena, gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields,
-	const gchar *thing);
+/* Constructor */
+
+GtkWidget *ui_arena_new(Map *map);
+
+/* Methods */
+
+void    ui_arena_draw      (UIArena *arena);
+
+void    ui_arena_set_map   (UIArena *arena, Map *map);
+Map    *ui_arena_get_map   (UIArena *self);
 
 G_END_DECLS
 
diff --git a/include/ui-status.h b/include/ui-status.h
new file mode 100644
index 0000000..ea0f336
--- /dev/null
+++ b/include/ui-status.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2008 Bradley Smith <brad@brad-smith.co.uk>
+ *
+ * GNU Robots, ui-arena.h
+ *
+ * GNU Robots is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GNU Robots is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UI_STATUS_H__
+#define __UI_STATUS_H__
+
+#include <gtk/gtk.h>
+
+#include "grobot.h"
+
+G_BEGIN_DECLS
+
+/** Standard GObject class.  Necessary to make UI_TYPE_STATUS work,
+ *  for instance.
+ */
+GType ui_status_get_type(void) G_GNUC_CONST;
+
+#define UI_TYPE_STATUS ui_status_get_type()
+/** Use this to cast to UIStatus.
+ */
+#define UI_STATUS(obj)						\
+    G_TYPE_CHECK_INSTANCE_CAST(obj, UI_TYPE_STATUS, UIStatus)
+/** Use this to test whether this is a valid UIStatus object.
+ */
+#define IS_UI_STATUS(obj) \
+    G_TYPE_CHECK_INSTANCE_TYPE(obj, UI_TYPE_STATUS)
+
+/** Main typedef for the UIStatus object.
+ */
+typedef struct _UIStatus UIStatus;
+/** Main typedef for the UIStatus class object.
+ */
+typedef struct _UIStatusClass UIStatusClass;
+/** Main typedef for the UIStatus private data.
+ */
+typedef struct _UIStatusPrivate UIStatusPrivate;
+
+/** Main struct for the UIStatus object.
+ */
+struct _UIStatus
+{
+  GtkGrid widget;       /*!< the UIStatus's parent instance. */
+  UIStatusPrivate* priv; /*!< Private data for UIStatus. */
+};
+
+/** Class struct for UIStatus.
+ */
+struct _UIStatusClass
+{
+  GtkGridClass parent_class; /*!< Pointer to the parent class instance. */
+};
+
+/** Constructor to create a new UIStatus object.
+ *
+ * \returns the UIStatus cast as a GtkWidget.
+ */
+GtkWidget *ui_status_new      ();
+
+/** Function to set the robot for UIStatus.
+ *
+ * \param[in, out] self the object to set the robot on.
+ * \param[in]      robot the robot to set on UIStatus.
+ */
+void       ui_status_set_robot  (UIStatus *self, GRobot *robot);
+/** Function to get the robot for UIStatus.
+ *
+ * \param[in] self the object to inquire on.
+ *
+ * \returns the GRobot that is associated with this UIStatus object.
+ */
+GRobot*    ui_status_get_robot  (UIStatus *self);
+
+G_END_DECLS
+
+#endif
diff --git a/include/ui-window-private.h b/include/ui-window-private.h
deleted file mode 100644
index f4ba459..0000000
--- a/include/ui-window-private.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (C) 2008 Bradley Smith <brad@brad-smith.co.uk>
- *
- * GNU Robots, ui-window-private.h.
- *
- * GNU Robots is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GNU Robots is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __UI_WINDOW_PRIVATE_H__
-#define __UI_WINDOW_PRIVATE_H__
-
-G_BEGIN_DECLS
-
-struct _UIWindowPrivate
-{
-	GtkWidget *cmdwin;
-	GtkWidget *arena;
-};
-
-G_END_DECLS
-
-#endif /* __UI_WINDOW_PRIVATE_H__ */
diff --git a/include/ui-window.h b/include/ui-window.h
index c8fa337..f2e32e1 100644
--- a/include/ui-window.h
+++ b/include/ui-window.h
@@ -19,42 +19,52 @@
 #ifndef __UI_WINDOW_H__
 #define __UI_WINDOW_H__
 
-#include "map.h"
-
-#include <gtk/gtkwindow.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include "grobot.h"
 
 G_BEGIN_DECLS
 
-#define UI_TYPE_WINDOW \
-    ui_window_get_type()
-#define UI_WINDOW(obj) \
-    G_TYPE_CHECK_INSTANCE_CAST(obj, UI_TYPE_WINDOW, UIWindow)
-#define UI_WINDOW_CLASS(klass) \
-    G_TYPE_CHECK_CLASS_CAST(klass, UI_WINDOW_TYPE, UIWindowClass)
-#define IS_UI_WINDOW(obj) \
-    G_TYPE_CHECK_INSTANCE_TYPE(obj, UI_TYPE_WINDOW)
-#define IS_UI_WINDOW_CLASS(klass) \
-    G_TYPE_CHECK_CLASS_TYPE(klass, UI_TYPE_WINDOW)
-
-typedef struct _UIWindow UIWindow;
-typedef struct _UIWindowClass UIWindowClass;
+#define UI_WINDOW_TYPE              (ui_window_get_type())
+#define UI_WINDOW(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), UI_WINDOW_TYPE, UIWindow))
+#define UI_WINDOW_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), UI_WINDOW_TYPE, UIWindowClass))
+#define UI_WINDOW_IS_UI_WINDOW(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), UI_WINDOW_TYPE))
+#define UI_WINDOW_IS_UI_WINDOW_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), UI_WINDOW_TYPE))
+#define UI_WINDOW_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), UI_WINDOW_TYPE, UIWindowClass))
+
+typedef struct _UIWindow        UIWindow;
+typedef struct _UIWindowClass   UIWindowClass;
 typedef struct _UIWindowPrivate UIWindowPrivate;
 
 struct _UIWindow
 {
-  GtkWindow widget;
-  UIWindowPrivate* priv;
+  GtkApplicationWindow window;
+
+  /*< private > */
+  UIWindowPrivate *priv;
 };
 
 struct _UIWindowClass
 {
-  GtkWindowClass parent_class;
+  GtkApplicationWindowClass parent_class;
 };
 
-GType ui_window_get_type(void) G_GNUC_CONST;
-
-GtkWidget *ui_window_new();
-void ui_window_postinit(UIWindow *window, Map* volatile* map);
+/** Initialize and kick-off the GUI process.  Does not return until
+ *  GTK exits.
+ *
+ * \param[in, out] map The map to display in ui-arena.
+ * \param[in, out] robot the robot to track in ui-stats.
+ *
+ * \returns the status from running the GtkApplication
+ */
+gint          ui_window_main(Map *map, GRobot *robot, gint argc, gchar **argv);
+/** Returns the GOptionGroup to be used in commandline processing.
+ *
+ * The GOptionGroup is found by calling gtk_get_option_group.
+ *
+ * Using this should render calling gtk_init unnecessary.
+ */
+GOptionGroup* ui_window_gtk_option_group();
 
 G_END_DECLS
 
diff --git a/maps/challenge_backtrack.map b/maps/challenge_backtrack.map
new file mode 100644
index 0000000..c31046e
--- /dev/null
+++ b/maps/challenge_backtrack.map
@@ -0,0 +1,7 @@
+###############
+#   +#####+   #
+#    #####    #
+#      R      #
+#@  $#####$  @#
+#    #####    #
+###############
diff --git a/maps/challenge_baddies.map b/maps/challenge_baddies.map
new file mode 100644
index 0000000..cb97a27
--- /dev/null
+++ b/maps/challenge_baddies.map
@@ -0,0 +1,8 @@
+###############################
+#                             #
+# @@@@@@@@@@@@ @ @@@@@@@@@@@@ #
+#$             R             $#
+# @@@@@@@@@@@@   @@@@@@@@@@@@ #
+#                             #
+###############################
+
diff --git a/maps/challenge_blocked.map b/maps/challenge_blocked.map
new file mode 100644
index 0000000..58e23c3
--- /dev/null
+++ b/maps/challenge_blocked.map
@@ -0,0 +1,7 @@
+###########
+#    #    #
+#    @    #
+#  R @   $#
+#    @    #
+#    #    #
+###########
diff --git a/maps/challenge_tiny.map b/maps/challenge_tiny.map
new file mode 100644
index 0000000..1cbe7a6
--- /dev/null
+++ b/maps/challenge_tiny.map
@@ -0,0 +1,7 @@
+######
+#   +#
+#    #
+#  R #
+#@  $#
+#    #
+######
diff --git a/maps/pattern.map b/maps/pattern.map
index ec1f29d..021a4e5 100644
--- a/maps/pattern.map
+++ b/maps/pattern.map
@@ -1,5 +1,5 @@
 ##############################################
-#+ @    $#+ @    $#+ @    $#+ @    $#+ @    $#
+#  @    $#+ @    $#+ @    $#+ @    $#+ @    $#
 #+   $#+ @    $#+ @    $#+ @    $#+ @    $#+$#
 #+ @    $#+ @    $#+ @    $#+ @    $#+ @    $#
 #+ $#+ @    $#+ @    $#+ @    $#+ @    $#+$#+#
diff --git a/maps/small.map b/maps/small.map
index 91fbe04..e2d286e 100644
--- a/maps/small.map
+++ b/maps/small.map
@@ -1,5 +1,5 @@
 ##########################
-#$$$$$$$$$$$$$$$$$$$$$$$$#
+# $$$$$$$$$$$$$$$$$$$$$$$#
 #$                      $#
 #$    ###          @@   $#
 #$      @   ++          $#
diff --git a/scheme/Makefile.am b/scheme/Makefile.am
index df70bc8..62e28a4 100644
--- a/scheme/Makefile.am
+++ b/scheme/Makefile.am
@@ -19,6 +19,7 @@ schemedir = $(pkgdatadir)/scheme
 
 scheme_DATA =\
  beep.scm\
+ bot-wander.scm\
  greedy.scm\
  mapper.scm\
  simple.scm\
@@ -27,6 +28,7 @@ scheme_DATA =\
 
 EXTRA_DIST =\
  beep.scm\
+ bot-wander.scm\
  greedy.scm\
  mapper.scm\
  simple.scm\
diff --git a/scheme/bot-wander.scm b/scheme/bot-wander.scm
new file mode 100644
index 0000000..730670c
--- /dev/null
+++ b/scheme/bot-wander.scm
@@ -0,0 +1,647 @@
+(use-modules
+ (srfi srfi-1)  ;; List functions (reduce, fold, alist-*, ...)
+ (srfi srfi-69) ;; Hash Table
+ )
+
+;;;;;;;;;;;;;;;;
+;; Helper Output Functions
+
+(define (map-info-print map-info)
+  (define (map-coords-list x-min x-max y-min y-max)
+    (define (iter x y accum)
+      (cond
+       ((< y y-min) accum)
+       ((< x x-min) (iter x-max (- y 1) accum))
+       (else (iter (- x 1) y (cons (list x y) accum)))))
+    (iter x-max y-max '()))
+  (define (loc-chars coords)
+    (let*
+	((loc-info (hash-table-ref/default map-info coords #f))
+	 (map-display-alist
+	  '(("space" . ".") ("food" . "+") ("prize" . "$")
+	    ("wall" . "#") ("baddie" . "@") ("robot" . "R")))
+	 (map-display-bot-alist ;; Info the robot added to the map
+	  '((not-visited . "?") (robot . "R") (inferred . "i")))
+	 (loc-type (and loc-info (assoc (car loc-info) map-display-alist)))
+	 (loc-bot-data (assoc (and loc-info (cadr loc-info)) map-display-bot-alist)))
+      (list (if loc-info (or (and loc-type (cdr loc-type)) "!") "*")
+	    (if loc-info (or (and loc-bot-data (cdr loc-bot-data)) " ") " "))))
+  (let
+      ((map-dims ;; x and y max and min
+	(fold
+	 (lambda (b a)
+	   (list (cons 'min-x (min (cdr (assoc 'min-x a)) (car  b)))
+		 (cons 'max-x (max (cdr (assoc 'max-x a)) (car  b)))
+		 (cons 'min-y (min (cdr (assoc 'min-y a)) (cadr b)))
+		 (cons 'max-y (max (cdr (assoc 'max-y a)) (cadr b)))))
+	 '((min-x . 0) (max-x . 0) (min-y . 0) (max-y . 0))
+	 (hash-table-keys map-info))))
+    (format #t "~A~%" map-dims)
+    (for-each
+     (lambda (c)
+       (if (= (car c) (cdr (assoc 'min-x map-dims)))
+	   (newline))
+       (let*
+	   ((loc-ch (loc-chars c)))
+	 (format #t "~1A~1A" (car loc-ch) (cadr loc-ch))))
+     (map-coords-list
+      (cdr (assoc 'min-x map-dims))
+      (cdr (assoc 'max-x map-dims))
+      (cdr (assoc 'min-y map-dims))
+      (cdr (assoc 'max-y map-dims))))
+    (newline)))
+
+(define (shortest-paths-print sp)
+  (let
+      ((min-vals (apply map min (hash-table-keys sp)))
+       (max-vals (apply map max (hash-table-keys sp))))
+    (format #t "Dims: (~d, ~d) -> (~d, ~d)~%"
+	    (car min-vals)  (car max-vals)
+	    (cadr min-vals) (cadr max-vals))
+    (for-each
+     (lambda (y)
+       (when (= y (cadr min-vals)) ;; Print x headings
+	 (format #t "     ")
+	 (for-each
+	  (lambda (x) (format #t "~4d  " x))
+	  (iota (1+ (- (car max-vals) (car min-vals))) (car min-vals)))
+	 (newline))
+       (format #t "~3d: " y) ;; Print y headings
+       (for-each
+	(lambda (x)
+	  (let
+	      ((north-south
+		(hash-table-ref/default sp (append (list x y) '(0 1)) #f))
+	       (east-west
+		(hash-table-ref/default sp (append (list x y) '(1 0)) #f)))
+	    (format #t "~4d~1A~1A"
+		    (reduce min -2
+			    (filter number? (list north-south east-west)))
+		    (if (and north-south
+			     (or (not east-west)
+				 (>= east-west north-south)))
+			"N" " ")
+		    (if (and east-west
+			     (or (not north-south)
+				 (>= north-south east-west)))
+			"E" " "))))
+	(iota (1+ (- (car max-vals) (car min-vals))) (car min-vals)))
+       (newline))
+     (iota (1+ (- (cadr max-vals) (cadr min-vals))) (cadr min-vals)))))
+
+;;;;;;;;;;;;;;;;
+;; Core Bot Logic
+
+;; Cardinal Directions are lists (x y) where the value in x and y are
+;; the offsets to use from the current position to reach the
+;; coordinate one away in that direction.
+
+;; All the directions in sequence so that clockwise turns will produce
+;; the next in the seq.
+(define directions-in-seq
+  '(( 1  0)
+    ( 0  1)
+    (-1  0)
+    ( 0 -1)))
+;; A clockwise circular (endless) representation of directions
+(define circular-directions
+  (apply circular-list directions-in-seq))
+
+;; Given a direction dir, turn-clockwise gives the cardinal direction
+;; for a 90 degree clockwise turn.
+(define (turn-clockwise dir flip)
+  (let
+      ((dir-n (find-tail
+	     (lambda (x) (equal? x dir))
+	     ((if flip reverse (lambda (l) l))
+	      (take circular-directions
+		    (1+ (length directions-in-seq)))))))
+    (if dir-n
+	(cadr dir-n)
+	;; This error should never happen; function handles this and
+	;; writes an error message to prevent an infinite loop.
+	(error turn-clockwise "unmatched dir given" dir))))
+
+;; Calculate the turns to turn to face dest-coords originating from
+;; coords and facing dir.
+;; coords and dest-coords must be adjacent (if they are not, returns
+;; #f).
+;; Returns negative numbers for counter-clockwise turning and positive
+;; numbers for clockwise turning.
+(define (turns-to-face coords starting-dir dest-coords)
+  (define (iter dir turns)
+    (cond
+     ((equal? (map + coords dir) dest-coords) turns)
+     ((> turns (length directions-in-seq))
+      (error (format #f "turns-to-face: coords ~A and ~A are not adjacent~%" coords dest-coords))
+      #f)
+     (else
+      (iter (turn-clockwise dir #f) (1+ turns)))))
+  (let
+      ((turns (iter starting-dir 0)))
+    (cond
+     ((not turns) #f)
+     ((> turns (abs (- turns (length directions-in-seq))))
+      (- turns (length directions-in-seq)))
+     (else turns))))
+
+(define (bot-turn starting-dir turns)
+  (cond
+   ((zero? turns) starting-dir)
+   (else
+    (let
+	((one-turn (if (> 0 turns) -1 1)))
+     (if (robot-turn one-turn)
+	 (bot-turn (turn-clockwise starting-dir (> 0 one-turn))
+		   (- turns one-turn))
+	 starting-dir)))))
+
+;; Perform a turn to a dest-coord requiring the fewest number of
+;; turns.
+;; Returns the new direction the robot is facing.
+(define (turn-to-best coords starting-dir dest-coords-list)
+  (cond
+   ((null? dest-coords-list) starting-dir)
+   (else
+    (let
+	((best-coords
+	  (reduce (lambda (best next-coords)
+		    (if (> (abs (turns-to-face coords starting-dir best))
+			   (abs (turns-to-face coords starting-dir next-coords)))
+			next-coords
+			best))
+		  starting-dir
+		  dest-coords-list)))
+      (bot-turn starting-dir (turns-to-face coords starting-dir best-coords))))))
+
+;; Returns a list of the coords adjacent the the given coords.
+(define (list-adjacent coords)
+  (map (lambda (c) (map + c coords))
+       directions-in-seq))
+
+;; Do as many robot-feel calls as needed to determine what is in front
+;; of the bot.
+(define (bot-feel)
+  (define (bot-feel-inner feel-for-list)
+    (cond
+     ((null? feel-for-list)
+      "prize-or-food")
+     (else
+      (let*
+	  ((starting-shields (robot-get-shields))
+	   (found (robot-feel (car feel-for-list))))
+	(if found
+	    (car feel-for-list)
+	    (if (< (robot-get-shields) starting-shields)
+		"baddie"
+		(bot-feel-inner (cdr feel-for-list))))))))
+  (bot-feel-inner
+   '("space" "wall" "robot")))
+
+(define (bot-greedy-feel)
+  (let ((felt (bot-feel)))
+    (cond
+     ((member felt '("prize-or-food" "prize" "food"))
+      (if (robot-grab)
+	  "space"
+	  felt))
+     (else felt))))
+
+(define (bot-move map-info coords direction coords-to)
+  (cond
+   ((not
+     (member coords-to (list (map + coords direction)
+			     (map - coords direction))))
+    (error
+     (format #f "bot-move: coords-to ~A is neither in front of nor behind coords ~A (direction ~A)"
+	     coords-to coords direction)))
+   ((robot-move (if (equal? (map + coords direction) coords-to)
+		    1 -1))
+    (hash-table-set! map-info coords-to
+		     (list
+		      (car (hash-table-ref/default map-info coords-to #f))
+		      'robot))
+    (hash-table-set! map-info coords
+		     (list
+		      (car (hash-table-ref/default map-info coords #f))
+		      'visited))
+    coords-to)
+   (else
+    (if (robot-is-alive?)
+	(error
+	 (format #f "bot-move: failed moving ~A (direction ~A) to ~A~%"
+		 coords direction coords-to))
+	coords))))
+
+;; If all a location's neighbors are known, then it does not need to
+;; be marked 'not-visited (i.e. no need to take the robot there).
+;; This should cascade to its unvisited neighbors.
+(define (map-info-infer! map-info initial-coords)
+  (define (coords-wall? coords)
+    (and
+     (hash-table-exists? map-info coords)
+     (equal? "wall" (car (hash-table-ref map-info coords)))))
+  (define (coords-known? coords)
+    (and (hash-table-exists? map-info coords)
+	 (string? (car (hash-table-ref map-info coords)))
+	 coords))
+  (define (map-info-infer-recur! to-check checked)
+    (cond
+     ((null? to-check) 'done)
+     (else
+      (let*
+	  ((coords (car to-check)))
+	(when
+	    (and
+	     (every coords-known? (list-adjacent coords))
+	     (not (coords-wall? coords)))
+	  (hash-table-set!
+	   map-info coords
+	   (list (car (hash-table-ref map-info coords)) 'inferred)))
+	(map-info-infer-recur!
+	 (append
+	  (cdr to-check)
+	  (filter
+	   (lambda (c)
+	     (and (hash-table-exists? map-info c)
+		  (eq? 'not-visited
+		       (cadr (hash-table-ref/default map-info c #f)))
+		  (not (coords-wall? c))
+		  (not (member c checked))))
+	   (list-adjacent coords)))
+	 (cons coords checked))))))
+  (map-info-infer-recur! (list initial-coords) '()))
+
+;;;;;;;;;;;;;;;;
+;; Dijkstra's Shortest Path Algorithm
+
+;; Use to calculate the best path for the robot to get to an
+;; interesting location.
+
+;; When there are no interesting adjacent locations, check map-info
+;; for interesting locations and go to the closest one.  Baddies
+;; should be engaged if there are no interesting spaces. They may be
+;; blocking interesting areas.
+
+;; shortest-paths produces a hash-table containing a direction-tagged
+;; (vector) and a cost.  Directions are only nort-south and east-west
+;; (it does not worry about which---assumtion is that the robot is
+;; willing to go forwards or backwards.
+
+;; Example location 2,3 facing east will have a key '(2 3 1 0) and a
+;; value of n (the cost to reach that location facing in that
+;; direction).
+(define (list-adjacent-vectors coords-vector)
+  (map (lambda (c)
+	 (append c (if (= (car c) (car coords-vector))
+		       '(0 1)
+		       '(1 0))))
+       (list-adjacent (take coords-vector 2))))
+
+(define (adjacent-move-cost coords-vector dest-coords-vector
+			    move-cost turn-cost)
+  (+ move-cost
+     (*
+      turn-cost
+      (if (equal? (drop coords-vector 2)
+		  (drop dest-coords-vector 2))
+	  0 1))))
+
+(define (shortest-paths map-coords coords dir move-cost turn-cost)
+  (let*
+      ((uncalced-dist 99999)
+       (visited (alist->hash-table (list (cons (append coords (map abs dir)) 0))))
+       (unvisited
+	(alist->hash-table
+	 (fold
+	  (lambda (c l)
+	    (alist-cons
+	     (append c '(0 1)) uncalced-dist
+	     (alist-cons (append c '(1 0)) uncalced-dist l)))
+	  '()
+	  (delete coords map-coords)))))
+    (define (iter current-coords-vector)
+      (define (calc-dist-and-set! adj-coords-vector)
+	(let*
+	    ((loc-hash (if (hash-table-exists? unvisited adj-coords-vector)
+			   unvisited visited))
+	     (new-dist (+ (adjacent-move-cost current-coords-vector
+					      adj-coords-vector
+					      move-cost turn-cost)
+			  (hash-table-ref
+			   unvisited current-coords-vector
+			   (lambda () (hash-table-ref visited current-coords-vector))))))
+	  (when (and (hash-table-exists? loc-hash adj-coords-vector)
+		     (< new-dist (hash-table-ref loc-hash adj-coords-vector)))
+	    (hash-table-set! loc-hash adj-coords-vector new-dist))))
+      (cond
+       ((zero? (hash-table-size unvisited)) visited)
+       (else
+	(for-each
+	 calc-dist-and-set!
+	 (list-adjacent-vectors current-coords-vector))
+
+	;; Move current-coords-vector from unvisited into visited
+	(hash-table-set! visited
+			 current-coords-vector
+			 (hash-table-ref
+			  unvisited current-coords-vector
+			  (lambda ()
+			    (hash-table-ref visited current-coords-vector))))
+	(hash-table-delete! unvisited current-coords-vector)))
+
+      ;; Find the next smallest cost location to continue from
+      (let
+	  ((next-coord
+	    (hash-table-fold
+	     unvisited
+	     (lambda (k v min-coords)
+	       (cond
+		((= v uncalced-dist) min-coords)
+		((< v
+		    (hash-table-ref/default unvisited min-coords uncalced-dist))
+		 k)
+		(else min-coords)))
+	     #f)))
+	(if next-coord
+	    (iter next-coord)
+	    visited)))
+    (iter (car (hash-table-keys visited)))))
+
+;;;;;;;;;;;;;;;;
+;; Shortest Path Helper Functions
+
+(define (shortest-path-distance-to sp coords)
+  (reduce min #f
+	  (filter
+	   number?
+	   (list
+	    (hash-table-ref/default sp (append coords '(1 0)) #f)
+	    (hash-table-ref/default sp (append coords '(0 1)) #f)))))
+
+(define (shortest-path-to sp coords move-cost turn-cost)
+  (define (recur path)
+    (let
+	((next
+	  (car
+	   (reduce
+	    (lambda (c l) (if (< (cdr c) (cdr l)) c l))
+	    '()
+	    (map
+	     (lambda (c)
+	       (cons c
+		     (+ (or
+			 (shortest-path-distance-to sp (take c 2))
+			 999999)
+			(adjacent-move-cost c (car path)
+					    move-cost turn-cost))))
+	     (list-adjacent-vectors (car path)))))))
+      (when (member (take next 2) path)
+	(error "shortest-path-to: cycle detected"))
+      (if (zero? (shortest-path-distance-to sp (take next 2)))
+	  path
+	  (recur (cons (take next 2) path)))))
+  (recur (list coords)))
+
+(define (shortest-path-nearest sp interesting-coords)
+  (reduce
+   (lambda (c l)
+     (if (< (shortest-path-distance-to sp c)
+	    (shortest-path-distance-to sp l))
+	 c l))
+   '()
+   interesting-coords))
+
+(define (shortest-path-to-nearest map-coords
+				  starting-coords starting-dir
+				  interesting-coords
+				  move-cost
+				  turn-cost)
+  (let
+      ((sp (shortest-paths map-coords starting-coords starting-dir move-cost turn-cost)))
+    (let
+	((next-path (shortest-path-to
+		     sp
+		     (shortest-path-nearest sp interesting-coords)
+		     move-cost
+		     turn-cost)))
+      ;; (when (< 13 (car starting-coords))
+      ;; 	(error "wtf"))
+      (list
+       (shortest-path-distance-to sp (last next-path))
+       next-path))))
+
+;;;;;;;;;;;;;;;;
+;; Bot Mainline
+
+(define (bot-main max-iter be-chatty)
+  (let
+      ((map-info (make-hash-table)))
+    (define (negate-coords-predicate p)
+      (lambda (coords)
+	(when (not (pair? coords))
+	  (error "negate-coords-predicate given a non-coords parameter ~A"
+		 coords))
+	(and (not (p coords))
+	     coords)))
+    (define (coords-known? coords)
+      (and (hash-table-exists? map-info coords)
+	   (string? (car (hash-table-ref map-info coords)))
+	   coords))
+    (define (coords-explored? coords)
+      (and
+       (hash-table-exists? map-info coords)
+       (memq (cadr (hash-table-ref map-info coords))
+	     '(visited inferred blocked))
+       coords))
+    (define (coords-space? coords)
+      (and
+       (hash-table-exists? map-info coords)
+       (equal? "space"
+	       (car
+		(hash-table-ref map-info coords)))))
+    (define (coords-baddie? coords)
+      (and
+       (hash-table-exists? map-info coords)
+       (equal? "baddie"
+	       (car
+		(hash-table-ref map-info coords)))
+       coords))
+    (define (coords-not-explored? coords)
+      ((negate-coords-predicate coords-explored?) coords))
+    (define (coords-not-explored-not-baddie? coords)
+      (and (coords-not-explored? coords)
+	   ((negate-coords-predicate coords-baddie?) coords)
+	   coords))
+    (define (coords-not-explored-baddie? coords)
+      (and (coords-not-explored? coords)
+	   (coords-baddie? coords)
+	   coords))
+    (define (coords-blocked? coords)
+      (and
+       (hash-table-exists? map-info coords)
+       (memq (cadr (hash-table-ref map-info coords))
+    	     '(blocked))
+       coords))
+    (define (bot-wander iter coords direction dest-coords)
+      (format #t "~A: coords ~A direction ~A~%" iter coords direction)
+      (when (and be-chatty (= 0 (remainder iter 20))) (map-info-print map-info))
+      (let
+	  ((facing-coords (map + coords direction))
+	   (behind-coords (map - coords direction)))
+	(cond
+	 ((not (robot-is-alive?))
+	  (format #t "Robot died~%")
+	  map-info)
+	 ((and (< 0 max-iter) (> iter max-iter)) ;; Bail out after a few iterations
+	  (format #t "Max iterations in bot-main reached (~d = ~d)~%"
+		  iter max-iter)
+	  map-info)
+
+	 ((not (coords-known? facing-coords))
+	  ;; The robot is facing a location we haven't figured out
+	  (and be-chatty (format #t "Feel ~A~%" facing-coords))
+	  (let ((felt (bot-greedy-feel)))
+	    (hash-table-set!
+	     map-info
+	     facing-coords
+	     (list felt
+		   (cond
+		    ((equal? felt "wall") 'blocked)
+		    ((equal? felt "baddie") 'not-visited)
+		    ((equal? felt "space") 'not-visited)
+		    (else 'other))))
+	    (map-info-infer! map-info facing-coords)
+	    (bot-wander (1+ iter) coords direction dest-coords)))
+
+	 ((any (lambda (c) (not (coords-known? c)))
+	       (list-adjacent coords))
+	  ;; We have more to learn about this location's neighbors
+	  (and be-chatty (format #t "Investigate neighbors of ~A~%" coords))
+	  (bot-wander (1+ iter) coords
+		      (turn-to-best
+		       coords direction
+		       (filter (lambda (c) (not (coords-known? c)))
+			       (list-adjacent coords)))
+		      dest-coords))
+
+	 ((and dest-coords
+	       (coords-baddie? dest-coords))
+	  ;; Next to a baddie to zap
+	  (and be-chatty
+	       (format #t "Next to a baddie ~A that is blocking us ~A~%"
+		   dest-coords coords))
+	  (if (not (equal? facing-coords dest-coords))
+	      (bot-wander (1+ iter) coords
+			  (turn-to-best coords direction (list dest-coords))
+			  dest-coords)
+	      (begin
+		(and be-chatty
+		     (format #t "Zapping from ~A to ~A~%" coords facing-coords))
+		(when (robot-zap)
+		  (hash-table-set! map-info
+				   facing-coords
+				   (list "space" 'not-visited)))
+		(bot-wander (1+ iter) coords direction #f))))
+
+	 ((or (coords-not-explored-not-baddie? facing-coords)
+	      (member dest-coords (list facing-coords behind-coords)))
+	  ;; Facing an un-visited, not known baddie location; move to it.
+	  (format #t "Moving ~A to ~A f ~A b ~A~%" coords
+		  (if (or (coords-not-explored-not-baddie? facing-coords)
+			 (equal? facing-coords dest-coords))
+		     facing-coords  ;; go forward
+		     behind-coords ;; go backward
+		     )
+		  facing-coords behind-coords)
+	  (bot-wander (1+ iter)
+		      (bot-move map-info coords direction
+				(or dest-coords
+				    facing-coords))
+		      direction #f))
+
+	 ((any coords-not-explored-not-baddie? (list-adjacent coords))
+	  ;; There are un-visited, not known baddie neighboring locations
+	  ;; Turn to face a location we can explore
+	  (and be-chatty
+	       (format #t "Turn to face a better location ~A~%" coords))
+	  (bot-wander (1+ iter) coords
+		      (turn-to-best
+		       coords direction
+		       (filter coords-not-explored-not-baddie?
+			       (list-adjacent coords)))
+		      dest-coords))
+	 (else
+	  (let
+	      ((interesting-coords
+	  	(filter
+	  	 coords-not-explored-not-baddie?
+	  	 (delete coords (hash-table-keys map-info))))
+	       (baddie-blocked-coords
+	  	(filter
+	  	 coords-not-explored-baddie?
+	  	 (delete coords (hash-table-keys map-info)))))
+	    (cond
+	     ((and (null? interesting-coords)
+	    	   (null? baddie-blocked-coords))
+	      (format #t "Nothing left to do ~A~%" coords)
+	      map-info)
+	     (else
+	      (let*
+	    	  ((interesting-path
+	    	    (and
+		     (not (null? interesting-coords))
+		     (shortest-path-to-nearest
+	    	      (filter
+		       coords-space?
+		       (hash-table-keys map-info))
+		      coords direction
+		      interesting-coords
+	    	      (robot-get-move-energy-cost)
+		      (robot-get-turn-energy-cost))))
+		   (baddie-blocked-path
+		    (and
+		     (not (null? baddie-blocked-coords))
+	    	     (shortest-path-to-nearest
+	    	      (append
+		       (filter
+			coords-space?
+			(hash-table-keys map-info))
+		       baddie-blocked-coords)
+		      coords direction
+		      baddie-blocked-coords
+	    	      (robot-get-move-energy-cost)
+		      (robot-get-turn-energy-cost))))
+		   (new-dest
+		    (car
+		     (second
+		      (cond
+		       ((and interesting-path
+			     baddie-blocked-path)
+			(if (< (car interesting-path)
+			       (+ (car baddie-blocked-path)
+				  (robot-get-zap-energy-cost)))
+			    interesting-path baddie-blocked-path))
+		       (interesting-path interesting-path)
+		       (baddie-blocked-path baddie-blocked-path))))))
+		(and be-chatty
+		     (format #t "Heading to ~A~%" new-dest))
+	    	(bot-wander (1+ iter) coords
+	    		    (turn-to-best
+	    		     coords direction
+	    		     (list new-dest))
+	    		    new-dest))))
+	    )
+	  ))))
+    (hash-table-set! map-info '(0 0) '("space" visited)) ;; Add the starting location
+    (bot-wander 0 (list 0 0) (list 1 0) #f)
+    (map-info-print map-info)
+    map-info))
+
+(define (bot-start)
+  (bot-main 0 #f))
+
+;; TODO: The robot currently will never reverse.  If it wants to move
+;; backwards, it should reverse instead of turning unnecessarily.
+
+(bot-start)
diff --git a/scheme/simple.scm b/scheme/simple.scm
index eccabaf..479efd1 100644
--- a/scheme/simple.scm
+++ b/scheme/simple.scm
@@ -15,7 +15,11 @@
 
 ;;; Define a function to grab all prizes
 (define (grab-all-prizes)
-  (do () (not (feel-prize)) (grab-prize)))
+  (cond
+   ((feel-prize)
+    (grab-prize)
+    (grab-all-prizes))
+   (else #f)))
 
 ;;; The program starts here: hunt for all prizes
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 9373603..5f56b53 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,7 @@
 bin_PROGRAMS = gnurobots
 
 AM_CFLAGS = -Wall \
-	$(GUILE_CFLAGS) $(GTK2_CFLAGS) \
+	$(GUILE_CFLAGS) $(GTK_CFLAGS) \
 	$(VTE_CFLAGS) $(GTHREAD2_CFLAGS) \
 	-I$(top_builddir)/include \
    	-DPKGLIBDIR=\"$(pkglibdir)\" \
@@ -28,6 +28,6 @@ AM_CFLAGS = -Wall \
    	-DSCRIPTS_PATH=\"$(schemedir)\"
 
 gnurobots_SOURCES = main.c api.c map.c grobot.c \
-	ui-window.c ui-cmdwin.c ui-arena.c
+	ui-window.c ui-cmdwin.c ui-arena.c ui-status.c
 gnurobots_LDFLAGS = $(GUILE_LDFLAGS) $(GTHREAD2_LIBS) \
-	$(GTK2_LIBS) $(VTE_LIBS) -lutil
+	$(GTK_LIBS) $(VTE_LIBS) -lutil
diff --git a/src/api.c b/src/api.c
index 0274f48..96efb9b 100644
--- a/src/api.c
+++ b/src/api.c
@@ -17,35 +17,59 @@
  * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <stdio.h>
-#include <stdlib.h>             /* for abs, free */
+#include <stdlib.h>             /* for free */
 
 #include <glib.h>
 #include <libguile.h>           /* GNU Guile high */
 
-#include "api.h"                /* GNU Robots API */
-#include "main.h"               /* for exit_nicely */
+#include "config.h"
+#include "configs.h"
 #include "grobot.h"
+#include "api.h"                /* GNU Robots API */
+
+/** Robot object to be used in all the calls to the grobot.h
+ * functions.
+ */
+static GRobot *robot;
 
-extern GRobot *robot;
+/** If TRUE robot action functions will delay for USLEEP_TIME (mainly
+ * done to give time for animations to complete).
+ */
+static gboolean api_move_delay = TRUE;
 
 /* Functions */
 
-SCM
+static SCM
+api_set_move_delay_x (SCM s_b)
+{
+  api_move_delay = scm_is_true(s_b);
+
+  return(SCM_BOOL(scm_is_true(s_b)));
+}
+
+static SCM
 api_robot_turn (SCM s_n)
 {
-  g_robot_turn (robot, scm_to_int (s_n));
+  gboolean retval = g_robot_turn (robot, scm_to_int (s_n));
 
-  return SCM_BOOL (TRUE);
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+  
+  return SCM_BOOL (retval);
 }
 
-SCM
+static SCM
 api_robot_move (SCM s_n)
 {
-  return SCM_BOOL (g_robot_move (robot, scm_to_int (s_n)));
+  gboolean retval = g_robot_move (robot, scm_to_int (s_n));
+
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+
+  return SCM_BOOL (retval);
 }
 
-SCM
+static SCM
 api_robot_smell (SCM s_th)
 {
   gboolean ret;
@@ -57,10 +81,13 @@ api_robot_smell (SCM s_th)
   str = scm_to_locale_string (s_th);
   ret = g_robot_smell (robot, str);
 
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+
   return SCM_BOOL (ret);
 }
 
-SCM
+static SCM
 api_robot_feel (SCM s_th)
 {
   gboolean ret;
@@ -72,10 +99,13 @@ api_robot_feel (SCM s_th)
   str = scm_to_locale_string (s_th);
   ret = g_robot_feel (robot, str);
 
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+
   return SCM_BOOL (ret);
 }
 
-SCM
+static SCM
 api_robot_look (SCM s_th)
 {
   gboolean ret;
@@ -87,28 +117,41 @@ api_robot_look (SCM s_th)
   str = scm_to_locale_string (s_th);
   ret = g_robot_look (robot, str);
 
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+
   return SCM_BOOL (ret);
 }
 
-SCM
+static SCM
 api_robot_grab (void)
 {
-  return SCM_BOOL (g_robot_grab (robot));
+  gboolean retval = g_robot_grab (robot);
+
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+
+  return SCM_BOOL (retval);
 }
 
-SCM
+static SCM
 api_robot_zap (void)
 {
-  return SCM_BOOL (g_robot_zap (robot));
+  gboolean retval = g_robot_zap (robot);
+
+  if (api_move_delay)
+    g_usleep(USLEEP_TIME);
+
+  return SCM_BOOL (retval);
 }
 
-SCM
+static SCM
 api_robot_stop (void)
 {
   return SCM_BOOL (g_robot_stop (robot));
 }
 
-SCM
+static SCM
 api_robot_get_shields (void)
 {
   glong shields;
@@ -119,7 +162,7 @@ api_robot_get_shields (void)
   return (scm_from_long (shields));
 }
 
-SCM
+static SCM
 api_robot_get_energy (void)
 {
   glong energy;
@@ -130,7 +173,7 @@ api_robot_get_energy (void)
   return (scm_from_long (energy));
 }
 
-SCM
+static SCM
 api_robot_get_score (void)
 {
   glong score;
@@ -141,11 +184,157 @@ api_robot_get_score (void)
   return (scm_from_long (score));
 }
 
-void
-api_init (void)
+static SCM
+api_robot_is_alive (void)
+{
+  return SCM_BOOL (g_robot_is_alive (robot));
+}
+
+static SCM
+api_robot_get_move_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_move_energy_cost());
+}
+static SCM
+api_robot_get_turn_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_turn_energy_cost());
+}
+static SCM
+api_robot_get_feel_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_feel_energy_cost());
+}
+static SCM
+api_robot_get_smell_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_smell_energy_cost());
+}
+static SCM
+api_robot_get_look_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_look_energy_cost());
+}
+static SCM
+api_robot_get_grab_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_grab_energy_cost());
+}
+static SCM
+api_robot_get_zap_energy_cost(void)
+{
+  return scm_from_long (g_robot_get_zap_energy_cost());
+}
+
+/** Responsible for handling errors.
+ *
+ * \param[in] data
+ * \param[in] tag
+ * \param[in] throw_args
+ *
+ * \returns SCM_BOOL_F
+ */
+static SCM
+catch_handler(void *data, SCM tag, SCM throw_args)
+{
+  gchar *message = NULL;
+
+  if (scm_ilength (throw_args) > 1
+      && SCM_NFALSEP (scm_string_p (SCM_CADR (throw_args))))
+    {
+      message = scm_to_locale_string
+	(scm_simple_format (SCM_BOOL_F,
+			    SCM_CADR (throw_args),
+			    SCM_CADDR (throw_args)));
+    }
+
+  else if (SCM_NFALSEP (scm_symbol_p (tag)))
+    {
+      message = scm_to_locale_string (scm_symbol_to_string (tag));
+    }
+
+/*
+  user_interface_update_status (ui, message, -1, -1, -1);
+*/
+  if (message) {
+    g_print("Invalid Instruction: %s\n", message);
+    free (message);
+  } else {
+    g_print("Couldn't get error message\n");
+  }
+
+  return SCM_BOOL_F;
+}
+
+/** Helper function to load a file (robot_program) into Guile.
+ * 
+ * \param[in] robot_program a file name to load into Guile.
+ */
+static void
+api_load_robot_program(const gchar *robot_program)
+{
+  g_return_if_fail(robot_program != NULL);
+
+  if (strlen(robot_program) != 0)
+    {
+      /* execute a Scheme file */
+      g_print("Robot program: %s\n", robot_program);
+      scm_internal_catch(SCM_BOOL_T,
+			 (scm_t_catch_body) scm_c_primitive_load,
+			 (gpointer) robot_program,
+			 catch_handler, NULL);
+    }
+}
+
+/** Run Guile startup processes and imports.
+ * 
+ * When this function exits, the thread will call exit() for the
+ * program (terminating it).
+ *
+ * \param[in] robot_program a file name to load into Guile.
+ */
+static void
+api_repl_init(const gchar *robot_program)
+{
+  scm_internal_catch(SCM_BOOL_T,
+		     (scm_t_catch_body) scm_c_eval_string,
+		     "(sigaction SIGUSR1 (lambda (flags) (error \"User Interrupt\")))",
+		     catch_handler, NULL);
+  scm_internal_catch(SCM_BOOL_T,
+		     (scm_t_catch_body) scm_c_use_module,
+		     "ice-9 readline",
+		     catch_handler, NULL);
+  scm_internal_catch(SCM_BOOL_T,
+		     (scm_t_catch_body) scm_c_use_module,
+		     "gnurobots robot",
+		     catch_handler, NULL);
+  scm_internal_catch(SCM_BOOL_T,
+		     (scm_t_catch_body) scm_c_eval_string,
+		     "(activate-readline)",
+		     catch_handler, NULL);
+  scm_internal_catch(SCM_BOOL_T,
+		     (scm_t_catch_body) scm_c_use_module,
+		     "system repl repl",
+		     catch_handler, NULL);
+
+  if (NULL != robot_program)
+    api_load_robot_program(robot_program);
+
+  g_print("Entering interactive mode (REPL)...\n");
+  scm_internal_catch(SCM_BOOL_T,
+		     (scm_t_catch_body) scm_c_eval_string,
+		     "(start-repl)",
+		     catch_handler, NULL);
+}
+
+/** Define some new builtins (hooks) so that they are available in
+ * Scheme.
+ *
+ * \param[in] data Not used.
+ */
+static void
+api_define_procedures (gpointer data)
 {
-  /* define some new builtins (hooks) so that they are available in
-     Scheme. */
 
   /* Guile has this function set to expect that function-pointers are
      passed as (void *), which generates warnings because it's actually
@@ -156,17 +345,128 @@ api_init (void)
   typedef SCM (*c99_fakeout_guile)(const char *, int, int, int, SCM(*)());
   c99_fakeout_guile define_gsubr = (c99_fakeout_guile) scm_c_define_gsubr;
   define_gsubr ("robot-turn", 1, 0, 0, api_robot_turn);
+  scm_c_export("robot-turn");
   define_gsubr ("robot-move", 1, 0, 0, api_robot_move);
+  scm_c_export("robot-move");
   define_gsubr ("robot-smell", 1, 0, 0, api_robot_smell);
+  scm_c_export("robot-smell");
   define_gsubr ("robot-feel", 1, 0, 0, api_robot_feel);
+  scm_c_export("robot-feel");
   define_gsubr ("robot-look", 1, 0, 0, api_robot_look);
+  scm_c_export("robot-look");
   define_gsubr ("robot-grab", 0, 0, 0, api_robot_grab);
+  scm_c_export("robot-grab");
   define_gsubr ("robot-zap", 0, 0, 0, api_robot_zap);
+  scm_c_export("robot-zap");
 
   define_gsubr ("robot-get-shields", 0, 0, 0, api_robot_get_shields);
+  scm_c_export("robot-get-shields");
   define_gsubr ("robot-get-energy", 0, 0, 0, api_robot_get_energy);
+  scm_c_export("robot-get-energy");
   define_gsubr ("robot-get-score", 0, 0, 0, api_robot_get_score);
+  scm_c_export("robot-get-score");
+  define_gsubr ("robot-is-alive?", 0, 0, 0, api_robot_is_alive);
+  scm_c_export("robot-is-alive?");
+  define_gsubr ("robot-set-move-delay!", 1, 0, 0, api_set_move_delay_x);
+  scm_c_export("robot-set-move-delay!");
+
+  define_gsubr ("robot-get-move-energy-cost", 0, 0, 0,
+		api_robot_get_move_energy_cost);
+  scm_c_export("robot-get-move-energy-cost");
+  define_gsubr ("robot-get-turn-energy-cost", 0, 0, 0,
+		api_robot_get_turn_energy_cost);
+  scm_c_export("robot-get-turn-energy-cost");
+  define_gsubr ("robot-get-feel-energy-cost", 0, 0, 0,
+		api_robot_get_feel_energy_cost);
+  scm_c_export("robot-get-feel-energy-cost");
+  define_gsubr ("robot-get-smell-energy-cost", 0, 0, 0,
+		api_robot_get_smell_energy_cost);
+  scm_c_export("robot-get-smell-energy-cost");
+  define_gsubr ("robot-get-look-energy-cost", 0, 0, 0,
+		api_robot_get_look_energy_cost);
+  scm_c_export("robot-get-look-energy-cost");
+  define_gsubr ("robot-get-grab-energy-cost", 0, 0, 0,
+		api_robot_get_grab_energy_cost);
+  scm_c_export("robot-get-grab-energy-cost");
+  define_gsubr ("robot-get-zap-energy-cost", 0, 0, 0,
+		api_robot_get_zap_energy_cost);
+  scm_c_export("robot-get-zap-energy-cost");
 
   define_gsubr ("stop", 0, 0, 0, api_robot_stop);
+  scm_c_export("stop");
   define_gsubr ("quit", 0, 0, 0, api_robot_stop);
+  scm_c_export("quit");
+}
+
+/** The main program code that is executed after Guile starts up. 
+ *
+ * Pass the Scheme program as argv[1]. The program name is still
+ * argv[0].
+ *
+ * Attempts to load the scheme file if argc > 1 (using argv[1]).
+ *
+ * \param[in] closure not normally set (acceptable to pass NULL).
+ * \param[in] argc The number of arguments Guile can processin in argv.
+ * \param[in] argv The commandline arguments as strings.
+ */
+static void
+api_main_prog(void *closure, gint argc, gchar *argv[])
+{
+  scm_c_define_module("gnurobots robot", api_define_procedures, NULL);
+
+  gchar *robot_program = NULL;
+  if(argc > 1)
+    robot_program = argv[1];
+
+  api_repl_init(robot_program);
+}
+
+/** Structure to hold argc and argv to be passed to api_callback_scm.
+ */
+struct _ProgramArguments
+{
+  gint    argc; /*!< Number of arguments being passed in argv. */
+  gchar **argv; /*!< Arguments being passed as strings. */
+};
+
+/** Typedef to hold argc and argv to be passed to api_callback_scm.
+ */
+typedef struct _ProgramArguments ProgramArguments;
+
+/** Function to serve as the mainline in the Guile thread.
+ *
+ * \param[in] ProgramArguments struct to hold the values argc and argv
+ *            for processing.
+ *
+ * \returns NULL.  Note: does not normally return at all as the end of
+ *                 the Guile thread calls exit on the process.
+ */
+static gpointer
+api_callback_scm(ProgramArguments *args)
+{
+  scm_boot_guile(args->argc, args->argv, api_main_prog, NULL);
+
+  return NULL; /* never gets here, but keeps compiler happy */
+}
+
+void
+api_interrupt(void)
+{
+  raise(SIGUSR1);
+}
+
+GThread*
+api_init(gint argc, gchar **argv, gpointer in_robot)
+{
+  g_return_val_if_fail(G_IS_ROBOT(in_robot), NULL);
+
+  robot = g_object_ref(G_ROBOT(in_robot));
+
+  ProgramArguments *args = g_malloc(sizeof(ProgramArguments));
+  args->argc = argc;
+  args->argv = argv;
+
+  GThread *guile_thread = g_thread_new("Guile Thread", (GThreadFunc) api_callback_scm, args);
+
+  return guile_thread;
 }
diff --git a/src/grobot.c b/src/grobot.c
index 73e12a0..489e2f6 100644
--- a/src/grobot.c
+++ b/src/grobot.c
@@ -19,34 +19,49 @@
 
 #include "grobot.h"
 #include "configs.h"
-#include <stdio.h>
 #include <glib.h>
-#include <string.h>
-#include <stdlib.h>
+#include <stdarg.h>
 
-G_DEFINE_TYPE(GRobot, g_robot, G_TYPE_OBJECT)
+enum robot_signals
+{
+ DEATH,
+ STATS_UPDATE,
+ LAST_SIGNAL
+};
 
-enum
+enum robot_stats
 {
-	DEATH,
-	LAST_SIGNAL
+ ARG_0,
+ ARG_SCORE,
+ ARG_ENERGY,
+ ARG_SHIELDS,
+ ARG_UNITS,
+ ARG_SHOTS,
+ ARG_MAP
 };
 
-enum
-{
-	ARG_0,
-	ARG_POS_X,
-	ARG_POS_Y,
-	ARG_DIRECTION,
-	ARG_SCORE,
-	ARG_ENERGY,
-	ARG_SHIELDS,
-	ARG_UNITS,
-	ARG_SHOTS,
-	ARG_USER_INTERFACE,
-	ARG_MAP
+enum robot_actions
+{
+ ACTION_MOVE,
+ ACTION_TURN,
+ ACTION_FEEL,
+ ACTION_SMELL,
+ ACTION_LOOK,
+ ACTION_GRAB,
+ ACTION_ZAP,
+ ACTION_LAST_ACTION
 };
 
+static const glong action_energy_cost[ACTION_LAST_ACTION] =
+  {2, /* ACTION_MOVE */
+   2, /* ACTION_TURN */
+   1, /* ACTION_FEEL */
+   1, /* ACTION_SMELL */
+   1, /* ACTION_LOOK */
+   5, /* ACTION_GRAB */
+   10 /* ACTION_ZAP */
+  };
+
 static gchar *things[] = { "space", "food", "prize", "wall", "baddie",
 	"robot"
 };
@@ -54,750 +69,734 @@ static gint cthings[] = { SPACE, FOOD, PRIZE, WALL, BADDIE, ROBOT };
 
 static guint g_robot_signals[LAST_SIGNAL] = { 0 };
 
-static void g_robot_dispose(GObject *object);
-static void g_robot_finalize(GObject *object);
-
-static void g_robot_set_property(GObject *object, guint prop_id,
-	const GValue *value, GParamSpec *pspec);
+struct _GRobotPrivate
+{
+  GRobot_stats stats;
 
-static void g_robot_get_property(GObject *object, guint prop_id,
-	GValue *value, GParamSpec * pspec);
+  Map *map;
+};
 
-static GObjectClass *parent_class = NULL;
+G_DEFINE_TYPE_WITH_CODE(GRobot, g_robot, G_TYPE_OBJECT, G_ADD_PRIVATE(GRobot))
 
 static gint what_thing(const gchar *th);
 
-static void g_robot_init(GRobot *robot)
-{
-	/* Nothing yet, need to do priv stuff */
-}
-
-static void g_robot_class_init(GRobotClass *klass)
-{
-	GObjectClass *gobject_class;
-
-	gobject_class = (GObjectClass *) klass;
-
-	parent_class = g_type_class_ref(G_TYPE_OBJECT);
-
-	gobject_class->dispose = g_robot_dispose;
-	gobject_class->finalize = g_robot_finalize;
-	gobject_class->set_property = g_robot_set_property;
-	gobject_class->get_property = g_robot_get_property;
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_POS_X,
-		g_param_spec_int("x",
-			"x",
-			"X co-ordinate of current Position of the Robot",
-			G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_POS_Y,
-		g_param_spec_int("y",
-			"y",
-			"y co-ordinate of current Position of the Robot",
-			G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DIRECTION,
-		g_param_spec_int("direction",
-			"direction",
-			"current Direction of the Robot",
-			G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SCORE,
-		g_param_spec_long("score",
-			"Score",
-			"current Score of the Robot",
-			G_MINLONG, G_MAXLONG, 0,
-			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_ENERGY,
-		g_param_spec_long("energy",
-			"Energy",
-			"current Energy-level of the Robot",
-			G_MINLONG, G_MAXLONG, 0,
-			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SHIELDS,
-		g_param_spec_long("shields",
-			"Shields",
-			"current Shield-level of the Robot",
-			G_MINLONG, G_MAXLONG, 0,
-			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_UNITS,
-		g_param_spec_long("units",
-			"Units",
-			"Units walked by the Robot so far",
-			G_MINLONG, G_MAXLONG, 0,
-			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SHOTS,
-		g_param_spec_long("shots",
-			"Shots",
-			"Number of Shots fired by the Robot",
-			G_MINLONG, G_MAXLONG, 0,
-			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass),
-		ARG_USER_INTERFACE,
-		g_param_spec_object("user-interface",
-			"UserInterface",
-			"Reference to the UI object",
-			G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MAP,
-		g_param_spec_object("map",
-			"Map",
-			"Reference to the Game Map object",
-			G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-	g_robot_signals[DEATH] =
-		g_signal_new("death",
-		G_TYPE_FROM_CLASS(klass),
-		G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET(GRobotClass, death),
-		NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
-}
-
 static void
 g_robot_set_property(GObject *object, guint prop_id, const GValue *value,
 	GParamSpec *pspec)
 {
-	GRobot *robot;
-	GObject *obj;
-
-	/* it's not null if we got it, but it might not be ours */
-	g_return_if_fail(G_IS_ROBOT(object));
-
-	robot = G_ROBOT(object);
-
-	switch (prop_id)
+  GRobot *robot;
+  GObject *obj;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(G_IS_ROBOT(object));
+
+  robot = G_ROBOT(object);
+
+  switch (prop_id)
+    {
+    case ARG_SCORE:
+      robot->priv->stats.score = g_value_get_long(value);
+      break;
+    case ARG_ENERGY:
+      robot->priv->stats.energy = g_value_get_long(value);
+      break;
+    case ARG_SHIELDS:
+      robot->priv->stats.shields = g_value_get_long(value);
+      break;
+    case ARG_SHOTS:
+      robot->priv->stats.shots = g_value_get_long(value);
+      break;
+    case ARG_UNITS:
+      robot->priv->stats.units = g_value_get_long(value);
+      break;
+    case ARG_MAP:
+      if (robot->priv->map != NULL)
 	{
-	case ARG_POS_X:
-		robot->x = g_value_get_int(value);
-		break;
-	case ARG_POS_Y:
-		robot->y = g_value_get_int(value);
-		break;
-	case ARG_DIRECTION:
-		robot->dir = g_value_get_int(value);
-		break;
-	case ARG_SCORE:
-		robot->score = g_value_get_long(value);
-		break;
-	case ARG_ENERGY:
-		robot->energy = g_value_get_long(value);
-		break;
-	case ARG_SHIELDS:
-		robot->shields = g_value_get_long(value);
-		break;
-	case ARG_SHOTS:
-		robot->shots = g_value_get_long(value);
-		break;
-	case ARG_UNITS:
-		robot->units = g_value_get_long(value);
-		break;
-	case ARG_USER_INTERFACE:
-		if (robot->ui != NULL)
-		{
-			g_object_unref(robot->ui);
-		}
-
-		obj = g_value_get_object(value);
-		if (obj != NULL)
-		{
-			robot->ui = g_object_ref(obj);
-		}
-
-		else
-		{
-			robot->ui = NULL;
-		}
-		break;
-	case ARG_MAP:
-		if (robot->map != NULL)
-		{
-			g_object_unref(robot->map);
-		}
-
-		obj = g_value_get_object(value);
-		if (obj != NULL)
-		{
-			robot->map = g_object_ref(obj);
-		}
+	  g_object_unref(robot->priv->map);
+	}
 
-		else
-		{
-			robot->map = NULL;
-		}
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-		break;
+      obj = g_value_get_object(value);
+      if (obj != NULL)
+	{
+	  robot->priv->map = MAP(g_object_ref(obj));
+	}
+      else
+	{
+	  robot->priv->map = NULL;
 	}
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+      break;
+    }
 }
 
 static void
 g_robot_get_property(GObject *object, guint prop_id, GValue *value,
 	GParamSpec *pspec)
 {
-	GRobot *robot;
-
-	/* it's not null if we got it, but it might not be ours */
-	g_return_if_fail(G_IS_ROBOT(object));
-
-	robot = G_ROBOT(object);
-
-	switch (prop_id)
-	{
-	case ARG_POS_X:
-		g_value_set_int(value, robot->x);
-		break;
-	case ARG_POS_Y:
-		g_value_set_int(value, robot->y);
-		break;
-	case ARG_DIRECTION:
-		g_value_set_int(value, robot->dir);
-		break;
-	case ARG_SCORE:
-		g_value_set_long(value, robot->score);
-		break;
-	case ARG_ENERGY:
-		g_value_set_long(value, robot->energy);
-		break;
-	case ARG_SHIELDS:
-		g_value_set_long(value, robot->shields);
-		break;
-	case ARG_SHOTS:
-		g_value_set_long(value, robot->shots);
-		break;
-	case ARG_UNITS:
-		g_value_set_long(value, robot->units);
-		break;
-	case ARG_USER_INTERFACE:
-		g_value_set_object(value, g_object_ref(G_OBJECT(robot->ui)));
-		break;
-	case ARG_MAP:
-		g_value_set_object(value, g_object_ref(G_OBJECT(robot->map)));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-		break;
-	}
+  GRobot *robot;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(G_IS_ROBOT(object));
+
+  robot = G_ROBOT(object);
+
+  switch (prop_id)
+    {
+    case ARG_SCORE:
+      g_value_set_long(value, robot->priv->stats.score);
+      break;
+    case ARG_ENERGY:
+      g_value_set_long(value, robot->priv->stats.energy);
+      break;
+    case ARG_SHIELDS:
+      g_value_set_long(value, robot->priv->stats.shields);
+      break;
+    case ARG_SHOTS:
+      g_value_set_long(value, robot->priv->stats.shots);
+      break;
+    case ARG_UNITS:
+      g_value_set_long(value, robot->priv->stats.units);
+      break;
+    case ARG_MAP:
+      g_value_set_object(value, g_object_ref(G_OBJECT(robot->priv->map)));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+      break;
+    }
 }
 
-GRobot *g_robot_new(gint x, gint y, gint dir, glong score, glong energy,
-	glong shields, glong units, glong shots, UIArena *ui, Map *map)
+static void
+g_robot_dispose(GObject *object)
 {
+  if (G_ROBOT(object)->priv->map != NULL)
+    {
+      g_object_unref(G_OBJECT(G_ROBOT(object)->priv->map));
+    }
 
-	return G_ROBOT(g_object_new(g_robot_get_type(),
-			"x", x,
-			"y", y,
-			"direction", dir,
-			"score", score,
-			"energy", energy,
-			"shields", shields,
-			"units", units,
-			"shots", shots, "user_interface", ui, "map", map, NULL));
+  G_OBJECT_CLASS(g_robot_parent_class)->dispose(object);
 }
 
-static void g_robot_dispose(GObject *object)
+static void
+g_robot_finalize(GObject *object)
 {
-	GRobot *robot = G_ROBOT(object);
-
-	if (robot->ui != NULL)
-	{
-		g_object_unref(G_OBJECT(robot->ui));
-	}
-
-	if (robot->map != NULL)
-	{
-		g_object_unref(G_OBJECT(robot->map));
-	}
-
-	parent_class->dispose(object);
+  if (G_ROBOT(object)->priv->map != NULL)
+    {
+      g_object_unref(G_OBJECT(G_ROBOT(object)->priv->map));
+    }
+  G_OBJECT_CLASS(g_robot_parent_class)->finalize(object);
 }
 
-static void g_robot_finalize(GObject *object)
+static void
+g_robot_init(GRobot *self)
 {
-	parent_class->finalize(object);
+  self->priv = g_robot_get_instance_private(self);
 }
 
-void g_robot_turn(GRobot *robot, gint num_turns)
+static void
+g_robot_class_init(GRobotClass *klass)
 {
-	gint i;
-	gint incr;
-
-	/* turn left or right? */
-
-	incr = sign(num_turns);
-
-	for (i = 0; i < abs(num_turns); i++)
-	{
-		robot->dir += incr;
-
-		if (robot->dir > 3)
-		{
-			robot->dir = 0;
-		}
-
-		else if (robot->dir < 0)
-		{
-			robot->dir = 3;
-		}
-
-		/* animate the robot */
-		ui_arena_move_robot(robot->ui, robot->x, robot->y, robot->x,
-			robot->y, robot->dir, robot->energy,
-			robot->score, robot->shields);
-
-		robot->energy -= 2;
-
-		if (robot->energy < 1)
-		{
-			g_signal_emit(robot, g_robot_signals[DEATH], 0);
-		}
-	}							/* for */
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->dispose = g_robot_dispose;
+  gobject_class->finalize = g_robot_finalize;
+  gobject_class->set_property = g_robot_set_property;
+  gobject_class->get_property = g_robot_get_property;
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), ARG_SCORE,
+     g_param_spec_long("score",
+		       "Score",
+		       "current Score of the Robot",
+		       0, G_MAXLONG, 0,
+		       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), ARG_ENERGY,
+     g_param_spec_long("energy",
+		       "Energy",
+		       "current Energy-level of the Robot",
+		       G_MINLONG, G_MAXLONG, DEFAULT_ENERGY,
+		       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), ARG_SHIELDS,
+     g_param_spec_long("shields",
+		       "Shields",
+		       "current Shield-level of the Robot",
+		       G_MINLONG, G_MAXLONG, DEFAULT_SHIELDS,
+		       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), ARG_UNITS,
+     g_param_spec_long("units",
+		       "Units",
+		       "Units walked by the Robot so far",
+		       0, G_MAXLONG, 0,
+		       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), ARG_SHOTS,
+     g_param_spec_long("shots",
+		       "Shots",
+		       "Number of Shots fired by the Robot",
+		       0, G_MAXLONG, 0,
+		       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), ARG_MAP,
+     g_param_spec_object("map",
+			 "Map",
+			 "Reference to the Game Map object",
+			 G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_robot_signals[DEATH] =
+    g_signal_new("death",
+		 G_TYPE_FROM_CLASS(klass),
+		 G_SIGNAL_RUN_LAST,
+		 G_STRUCT_OFFSET(GRobotClass, death),
+		 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
+  g_robot_signals[STATS_UPDATE] =
+    g_signal_new("robot-stats-update",
+		 G_TYPE_ROBOT,
+		 G_SIGNAL_ACTION,
+		 0,
+		 NULL /* accumulator */,
+		 NULL /* accumulator data */,
+		 NULL /* C marshaller */,
+		 G_TYPE_NONE /* return_type */,
+		 1    /* n_params */,
+		 G_TYPE_POINTER  /* param_types */);
 }
 
-gboolean g_robot_move(GRobot *robot, gint steps)
+GRobot*
+g_robot_new(glong energy, glong shields, glong units, glong shots, Map *map)
 {
-	gint x_to, y_to;
-	gint dx = 0, dy = 0;
-	gint i;
-
-	g_return_val_if_fail(robot->ui != NULL && robot->map != NULL, FALSE);
-
-	/* determine changes to x,y */
-
-	switch (robot->dir)
-	{
-	case NORTH:				/* N */
-		dx = 0;
-		dy = -1 * sign(steps);
-		break;
-	case EAST:					/* E */
-		dx = sign(steps);
-		dy = 0;
-		break;
-	case SOUTH:				/* S */
-		dx = 0;
-		dy = sign(steps);
-		break;
-	case WEST:					/* W */
-		dx = -1 * sign(steps);
-		dy = 0;
-		break;
-	}
-
-	/* Move the robot */
-	/* Arguably this should use energy even if steps=0, but it doesn't... */
-
-	for (i = 0; i < abs(steps); i++)
-	{
-		/* check for a space */
-
-		x_to = robot->x + dx;
-		y_to = robot->y + dy;
-
-		/* no matter what, this took energy */
-
-		robot->energy -= 2;
-
-		switch (MAP_GET_OBJECT(robot->map, x_to, y_to))
-		{
-		case SPACE:
-			/* move the robot there */
-			MAP_SET_OBJECT(robot->map, robot->x, robot->y, SPACE);
-			MAP_SET_OBJECT(robot->map, x_to, y_to, ROBOT);
+  return G_ROBOT(g_object_new(g_robot_get_type(),
+			      "score", 0,
+			      "energy", energy,
+			      "shields", shields,
+			      "units", units,
+			      "shots", shots,
+			      "map", map, NULL));
+}
 
-			break;
+gboolean
+g_robot_is_alive(GRobot *self)
+{
+  g_return_val_if_fail(G_IS_ROBOT(self), FALSE);
 
-		case BADDIE:
-			/* Damage */
-			robot->shields -= 10;
+  return(self->priv->stats.energy > 0 && self->priv->stats.shields > 0);
+}
 
-			x_to = robot->x;
-			y_to = robot->y;
+glong
+g_robot_get_turn(GRobot *self)
+{
+  g_return_val_if_fail(G_IS_ROBOT(self), -999);
 
-			break;
+  return(self->priv->stats.turn);
+}
 
-		case WALL:
-			/* less damage */
-			robot->shields -= 2;
+glong
+g_robot_get_shields(GRobot *self)
+{
+  g_return_val_if_fail(G_IS_ROBOT(self), -999);
 
-			x_to = robot->x;
-			y_to = robot->y;
+  return(self->priv->stats.shields);
+}
 
-			break;
+glong
+g_robot_get_score(GRobot *self)
+{
+  g_return_val_if_fail(G_IS_ROBOT(self), -999);
 
-		default:
-			/* even less damage */
-			robot->shields -= 1;
+  return(self->priv->stats.score);
+}
 
-			x_to = robot->x;
-			y_to = robot->y;
+glong
+g_robot_get_shots(GRobot *self)
+{
+  g_return_val_if_fail(G_IS_ROBOT(self), -999);
 
-			break;
-		}
+  return(self->priv->stats.shots);
+}
 
-		/* Animate/update the robot/arena/status regardless of
-		   whether the movement-attempt succeeded:
-		*/
-		ui_arena_move_robot(robot->ui, robot->x, robot->y, x_to, y_to,
-			robot->dir, robot->energy, robot->score, robot->shields);
+glong
+g_robot_get_energy(GRobot *self)
+{
+  g_return_val_if_fail(G_IS_ROBOT(self), -999);
 
-		if (robot->energy < 1 || robot->shields < 1)
-		{
-			g_signal_emit(robot, g_robot_signals[DEATH], 0);
-		}
+  return(self->priv->stats.energy);
+}
 
-		if (x_to == robot->x && y_to == robot->y) {
-			/* NOTE: by returning here, we charge only 1 hit
-			   against energy and shields, regardless of
-			   how many spaces the robot was attempting to go....
-			*/
-			return FALSE;
-		}
+glong
+g_robot_get_move_energy_cost()
+{
+  return action_energy_cost[ACTION_MOVE];
+}
 
-		robot->x = x_to;
-		robot->y = y_to;
-		robot->units++;
-	}							/* for */
+glong
+g_robot_get_turn_energy_cost()
+{
+  return action_energy_cost[ACTION_TURN];
+}
 
-	return TRUE;
+glong
+g_robot_get_feel_energy_cost()
+{
+  return action_energy_cost[ACTION_FEEL];
 }
 
-gboolean g_robot_smell(GRobot *robot, gchar *str)
+glong
+g_robot_get_smell_energy_cost()
 {
-	gint th;
-	gint i, j;
+  return action_energy_cost[ACTION_SMELL];
+}
 
-	g_return_val_if_fail(robot->ui != NULL && robot->map != NULL, FALSE);
+glong
+g_robot_get_look_energy_cost()
+{
+  return action_energy_cost[ACTION_LOOK];
+}
 
-	th = what_thing(str);
+glong
+g_robot_get_grab_energy_cost()
+{
+  return action_energy_cost[ACTION_GRAB];
+}
 
-	/* no matter what, this took energy */
+glong
+g_robot_get_zap_energy_cost()
+{
+  return action_energy_cost[ACTION_ZAP];
+}
 
-	if (--robot->energy < 1)
+/** Function to perform updates to robot statistics and to emit a
+ * signal on every status update.
+ *
+ * It's important to always use this function to modify the robot's
+ * statistics because:
+ *
+ * 1. It ensures consistent signal emission.
+ * 2. It performs testing and emits the robot-death signal if appropriate.
+ * 3. It automatically increments turns.
+ *
+ * Call the function using the robot_stats enum along with the new
+ * value to set.  
+ *
+ * The arguments will always come in pairs.  The following will set
+ * energy to 900 and set score to 5:
+ *
+ *    g_robot_stats_update(robot, "robot-move", 4, ARG_ENERGY, 900, ARG_SCORE, 5);
+ *
+ * \note It is vital that num_args be correctly set.
+ *
+ * \param [in, out] self the robot to update.
+ * \param [in] status_message describing the change being made
+ *        (usually something like "robot-move" to indicate that the
+ *        robot being moved is the reason for the stats update).  It
+ *        is safe to free this after making this call.
+ * \param [in] num_args the number of arguments that follow.
+ * \param [in] ... pairs of robot_stats enum entries (e.g. ARG_SCORE,
+ * ARG_ENERGY) along with the values to set them to.
+ *
+ * \returns TRUE if it succeeds; FALSE otherwise.
+ */
+static gboolean
+g_robot_stats_update(GRobot *self,
+		     gchar *status_message,
+		     gint num_args,
+		     ...)
+{
+  g_return_val_if_fail(num_args % 2 == 0, FALSE);
+
+  /* Always update turn. */
+  self->priv->stats.turn = self->priv->stats.turn + 1;
+
+  GRobot_change *change;
+  change = g_malloc(sizeof(GRobot_change));
+  g_strlcpy(change->status_message, status_message, ROBOT_STATUS_MESSAGE_LEN);
+  memcpy(&(change->before_change), self->priv, sizeof(GRobot_stats));
+
+  va_list       intArgumentPointer;
+  va_start( intArgumentPointer, num_args );
+  gint arg_stat;
+  gint arg_value;
+  for(gint i = 0; i < (num_args / 2); i++ )
+    {
+      arg_stat = va_arg(intArgumentPointer, int);
+      arg_value = va_arg(intArgumentPointer, int);
+
+      switch (arg_stat)
 	{
-		g_signal_emit(robot, g_robot_signals[DEATH], 0);
+	case ARG_SCORE:
+	  self->priv->stats.score = arg_value;
+	  break;
+	case ARG_ENERGY:
+	  self->priv->stats.energy = arg_value;
+	  break;
+	case ARG_SHIELDS:
+	  self->priv->stats.shields = arg_value;
+	  break;
+	case ARG_SHOTS:
+	  self->priv->stats.shots = arg_value;
+	  break;
+	case ARG_UNITS:
+	  self->priv->stats.units = arg_value;
+	  break;
+	default:
+	  g_critical("g_robot_stats_update: Unknown stat type: %d; value: %d", arg_stat, arg_value);
 	}
+      arg_stat = 0;
+    }
 
-	ui_arena_robot_smell(robot->ui, robot->x, robot->y, robot->dir,
-		robot->energy, robot->score, robot->shields,
-		str);
-
-	/* Smell for the thing */
-
-	for (i = robot->x - 1; i <= robot->x + 1; i++)
-	{
-		for (j = robot->y - 1; j <= robot->y + 1; j++)
-		{
-			if (!(i == robot->x && j == robot->y)
-				&& MAP_GET_OBJECT(robot->map, i, j) == th)
-			{
-				/* Found it */
-				return TRUE;
-			}
-		}						/* for */
-	}							/* for */
+  va_end( intArgumentPointer );
+  memcpy(&(change->after_change), self->priv, sizeof(GRobot_stats));
+  g_signal_emit(self, g_robot_signals[STATS_UPDATE], 0, change);
 
-	/* Failed to find it */
+  if(!g_robot_is_alive(self))
+    g_signal_emit(self, g_robot_signals[DEATH], 0);
+  g_free(change);
 
-	return FALSE;
+  return TRUE;
 }
 
-gboolean g_robot_feel(GRobot *robot, gchar *str)
+gboolean
+g_robot_turn(GRobot *self, gint num_turns)
 {
-	gint th;
-	gint x_to, y_to;
-	gint dx = 0, dy = 0;
+  g_return_val_if_fail(G_IS_ROBOT(self), FALSE);
 
-	g_return_val_if_fail(robot->ui != NULL && robot->map != NULL, FALSE);
+  if(!g_robot_is_alive(self))
+    return FALSE;
 
-	th = what_thing(str);
+  if(num_turns == 0)
+    return TRUE;
 
-	/* determine changes to x,y */
-	switch (robot->dir)
-	{
-	case NORTH:				/* N */
-		dx = 0;
-		dy = -1;
-		break;
-	case EAST:					/* E */
-		dx = 1;
-		dy = 0;
-		break;
-	case SOUTH:				/* S */
-		dx = 0;
-		dy = 1;
-		break;
-	case WEST:					/* W */
-		dx = -1;
-		dy = 0;
-		break;
-	}
+  gint x, y, orientation;
 
-	/* no matter what, this took energy */
-	if (--robot->energy < 1)
-	{
-		g_signal_emit(robot, g_robot_signals[DEATH], 0);
-	}
-
-	/* Feel for the thing */
-	x_to = robot->x + dx;
-	y_to = robot->y + dy;
+  if(!map_find_thing(self->priv->map, self, &x, &y, &orientation))
+    {
+      g_warning("g_robot_turn: robot not found on map.");
+      return FALSE;
+    }
 
-	ui_arena_robot_feel(robot->ui, robot->x, robot->y, robot->dir, x_to,
-		y_to, robot->energy, robot->score, robot->shields, str);
+  gint turn = 1; /* Compute one turn so we can check energy each time. */
+  if(num_turns < 0)
+    turn = turn * -1;
 
-	if (MAP_GET_OBJECT(robot->map, x_to, y_to) == BADDIE)
-	{
-		/* touching a baddie is hurtful */
-		if (robot->shields < 1)
-		{
-			g_signal_emit(robot, g_robot_signals[DEATH], 0);
-		}
-	}
+  if(!map_thing_turn_clockwise(self->priv->map, x, y, turn))
+    return FALSE;
 
-	if (MAP_GET_OBJECT(robot->map, x_to, y_to) == th)
-	{
-		return TRUE;
-	}
+  g_robot_stats_update(self, "robot-turn", 2,
+		       ARG_ENERGY, self->priv->stats.energy - action_energy_cost[ACTION_TURN]);
 
-	/* Did not feel it */
-	return FALSE;
+  return(g_robot_turn(self, num_turns - turn));
 }
 
-gboolean g_robot_look(GRobot *robot, gchar *str)
+gboolean
+g_robot_move(GRobot *robot, gint steps)
 {
-	gint th;
-	gint x_to, y_to;
-	gint dx = 0, dy = 0;
-
-	g_return_val_if_fail(robot->ui != NULL && robot->map != NULL, FALSE);
-
-	th = what_thing(str);
-
-	/* determine changes to x,y */
-	switch (robot->dir)
-	{
-	case NORTH:
-		dx = 0;
-		dy = -1;
-		break;
-	case EAST:
-		dx = 1;
-		dy = 0;
-		break;
-	case SOUTH:
-		dx = 0;
-		dy = 1;
-		break;
-	case WEST:
-		dx = -1;
-		dy = 0;
-		break;
-	}
+  g_return_val_if_fail(G_IS_ROBOT(robot), FALSE);
+  g_return_val_if_fail(robot->priv->map != NULL, FALSE);
+
+  if(!g_robot_is_alive(robot))
+    return FALSE;
+  if(steps == 0)
+    return TRUE;
+
+  gboolean retval = FALSE;
+
+  /* determine changes to x,y */
+  gint x_to, y_to, x, y, orientation;
+  if(!map_find_thing(robot->priv->map, robot, &x, &y, &orientation))
+    {
+      g_warning("g_robot_move: robot not found on map.");
+      return FALSE;
+    }
+
+  if(steps < 0) // We're going backwards, so invert the orientation
+    orientation = map_compute_clockwise_turn(orientation, 2);
+  map_compute_facing_coords(x, y, orientation, &x_to, &y_to);
+
+  gint shields_delta = 0;
+
+  switch (map_get_loc_thing_char(robot->priv->map, x_to, y_to))
+    {
+    case SPACE:
+      /* move the robot there */
+      retval = map_move(robot->priv->map, x, y, x_to, y_to);
+      break;
+    case BADDIE:
+      /* Damage */
+      shields_delta = shields_delta - 10;
+      break;
+    case WALL:
+      /* less damage */
+      shields_delta = shields_delta - 2;
+      break;
+    default:
+      /* even less damage */
+      shields_delta = shields_delta - 1;
+      break;
+    }
+
+  g_robot_stats_update(robot, "robot-move", 6,
+		       ARG_ENERGY, robot->priv->stats.energy - action_energy_cost[ACTION_MOVE],
+		       ARG_SHIELDS, robot->priv->stats.shields + shields_delta,
+		       ARG_UNITS, robot->priv->stats.units + 1);
+
+  if(!retval) // Bail out if we failed
+    return(retval);
+
+  gint step_adj = 1;
+  if(steps < 0)
+    step_adj = step_adj * -1;
+
+  // take more steps
+  return(g_robot_move(robot, steps - step_adj));
+}
 
-	/* no matter what, this took energy */
-	if (--robot->energy < 1)
-	{
-		g_signal_emit(robot, g_robot_signals[DEATH], 0);
-	}
+gboolean
+g_robot_smell(GRobot *robot, gchar *str)
+{
+  g_return_val_if_fail(G_IS_ROBOT(robot), FALSE);
+  g_return_val_if_fail(robot->priv->map != NULL, FALSE);
 
-	/* Look for the thing */
-	x_to = robot->x + dx;
-	y_to = robot->y + dy;
+  if(!g_robot_is_alive(robot))
+    return FALSE;
 
-	ui_arena_robot_look(robot->ui, robot->x, robot->y, robot->dir, x_to,
-		y_to, robot->energy, robot->score, robot->shields, str);
+  gboolean retval = FALSE;
+  gint th;
+  gint i, j;
 
-	while (MAP_GET_OBJECT(robot->map, x_to, y_to) == SPACE)
-	{
-		/* move the focus */
-		x_to += dx;
-		y_to += dy;
-	}
+  th = what_thing(str);
+  /* Determine Robot x, y */
+  gint x, y;
+  map_find_thing(robot->priv->map, robot, &x, &y, NULL);
 
-	/* Outside the loop, we have found something */
-	if (MAP_GET_OBJECT(robot->map, x_to, y_to) == th)
+  /* Smell for the thing */
+  for (i = x - 1; i <= x + 1; i++)
+    {
+      for (j = y - 1; j <= y + 1; j++)
 	{
-		return TRUE;
-	}
-
-	/* else, we did not find it */
-	return FALSE;
+	  if (!(i == x && j == y)
+	      && map_get_loc_thing_char(robot->priv->map, i, j) == th)
+	    {
+	      /* Found it */
+	      retval = TRUE;
+	    }
+	} /* for j */
+    } /* for i */
+
+  g_robot_stats_update(robot, "robot-smell", 2,
+		       ARG_ENERGY, robot->priv->stats.energy - action_energy_cost[ACTION_SMELL]);
+
+  return retval;
 }
 
-gboolean g_robot_grab(GRobot *robot)
+gboolean
+g_robot_feel(GRobot *robot, gchar *str)
 {
-	gint x_to, y_to;
-	gint dx = 0, dy = 0;
-
-	g_return_val_if_fail(robot->ui != NULL && robot->map != NULL, FALSE);
-
-	/* determine changes to x,y */
-
-	switch (robot->dir)
-	{
-	case NORTH:				/* N */
-		dx = 0;
-		dy = -1;
-		break;
-	case EAST:					/* E */
-		dx = 1;
-		dy = 0;
-		break;
-	case SOUTH:				/* S */
-		dx = 0;
-		dy = 1;
-		break;
-	case WEST:					/* W */
-		dx = -1;
-		dy = 0;
-		break;
-	}
-
-	/* Try to grab the thing */
+  g_return_val_if_fail(G_IS_ROBOT(robot), FALSE);
+  g_return_val_if_fail(robot->priv->map != NULL, FALSE);
 
-	x_to = robot->x + dx;
-	y_to = robot->y + dy;
+  if(!g_robot_is_alive(robot))
+    return FALSE;
 
-	robot->energy -= 5;
-	if (robot->energy < 1)
-	{
-		g_signal_emit(robot, g_robot_signals[DEATH], 0);
-	}
+  gboolean retval = FALSE;
 
-	ui_arena_robot_grab(robot->ui, robot->x, robot->y, robot->dir, x_to,
-		y_to, robot->energy, robot->score, robot->shields);
+  gint th;
+  th = what_thing(str);
 
-	/* Did we grab it? */
+  /* determine changes to x,y */
+  gint x_to, y_to, x, y, orientation;
+  map_find_thing(robot->priv->map, robot, &x, &y, &orientation);
 
-	switch (MAP_GET_OBJECT(robot->map, x_to, y_to))
-	{
-	case SPACE:
-	case WALL:
-	case ROBOT:
-		return FALSE;
-		break;
-
-	case BADDIE:
-		robot->shields -= 10;
-		if (robot->shields < 1)
-		{
-			g_signal_emit(robot, g_robot_signals[DEATH], 0);
-		}
-		return (FALSE);
+  map_compute_facing_coords(x, y, orientation, &x_to, &y_to);
 
-	case FOOD:
-		/* I want the net gain to be +10 */
-		robot->energy += 15;
-		break;
+  if (map_get_loc_thing_char(robot->priv->map, x_to, y_to) == th)
+    {
+      retval = TRUE;
+    }
 
-	case PRIZE:
-		robot->score++;
-		break;
-	}
+  /* touching a baddie is hurtful */
+  gint shields_delta = 0;
+  if (map_get_loc_thing_char(robot->priv->map, x_to, y_to) == BADDIE)
+    {
+      shields_delta = -2;
+    }
 
-	/* only successful grabs get here */
-	MAP_SET_OBJECT(robot->map, x_to, y_to, SPACE);
-	ui_arena_add_thing(robot->ui, x_to, y_to, SPACE);
+  g_robot_stats_update(robot, "robot-feel", 4,
+		       ARG_ENERGY, robot->priv->stats.energy - action_energy_cost[ACTION_FEEL],
+		       ARG_SHIELDS, robot->priv->stats.shields + shields_delta);
 
-	return TRUE;
+  return retval;
 }
 
-gboolean g_robot_zap(GRobot *robot)
+gboolean
+g_robot_look(GRobot *robot, gchar *str)
 {
-	gint x_to, y_to;
-	gint dx = 0, dy = 0;
+  g_return_val_if_fail(G_IS_ROBOT(robot), FALSE);
+  g_return_val_if_fail(robot->priv->map != NULL, FALSE);
 
-	g_return_val_if_fail(robot->ui != NULL && robot->map != NULL, FALSE);
+  if(!g_robot_is_alive(robot))
+    return FALSE;
 
-	/* determine changes to x,y */
+  gboolean retval = FALSE;
+  gint th;
 
-	switch (robot->dir)
-	{
-	case NORTH:				/* N */
-		dx = 0;
-		dy = -1;
-		break;
-	case EAST:					/* E */
-		dx = 1;
-		dy = 0;
-		break;
-	case SOUTH:				/* S */
-		dx = 0;
-		dy = 1;
-		break;
-	case WEST:					/* W */
-		dx = -1;
-		dy = 0;
-		break;
-	}
+  th = what_thing(str);
 
-	/* Try to zap the thing */
+  /* determine changes to x,y */
+  gint x_to, y_to, x, y, orientation;
+  map_find_thing(robot->priv->map, robot, &x, &y, &orientation);
 
-	x_to = robot->x + dx;
-	y_to = robot->y + dy;
+  map_compute_facing_coords(x, y, orientation, &x_to, &y_to);
 
-	robot->energy -= 10;
-	if (robot->energy < 1)
-	{
-		g_signal_emit(robot, g_robot_signals[DEATH], 0);
-	}
-	robot->shots++;
-	ui_arena_robot_zap(robot->ui, robot->x, robot->y, robot->dir, x_to,
-		y_to, robot->energy, robot->score, robot->shields);
+  while (map_get_loc_thing_char(robot->priv->map, x_to, y_to) == SPACE)
+    {
+      map_compute_facing_coords(x_to, y_to, orientation, &x_to, &y_to);
+    }
 
-	/* Did we destroy it? */
-	switch (MAP_GET_OBJECT(robot->map, x_to, y_to))
-	{
-	case SPACE:
-	case WALL:
-	case ROBOT:				/* what to w/ robots? */
-		return (FALSE);
-		break;
-
-	case BADDIE:
-	case FOOD:
-	case PRIZE:
-		ui_arena_add_thing(robot->ui, x_to, y_to, SPACE);
-		break;
-	}
+  /* Outside the loop, we have found something */
+  if (map_get_loc_thing_char(robot->priv->map, x_to, y_to) == th)
+    {
+      retval = TRUE;
+    }
 
-	/* only success gets here */
-	MAP_SET_OBJECT(robot->map, x_to, y_to, SPACE);
-	ui_arena_add_thing(robot->ui, x_to, y_to, SPACE);
+  g_robot_stats_update(robot, "robot-look", 2,
+		       ARG_ENERGY, robot->priv->stats.energy - action_energy_cost[ACTION_LOOK]);
 
-	return TRUE;
+  return retval;
 }
 
-gboolean g_robot_stop(GRobot *robot)
+gboolean
+g_robot_grab(GRobot *robot)
 {
-	/* Must be a SCM function, even though it returns no value */
-	/* Stop the robot immediately */
-
-	g_signal_emit(robot, g_robot_signals[DEATH], 0);
+  g_return_val_if_fail(G_IS_ROBOT(robot), FALSE);
+  g_return_val_if_fail(robot->priv->map != NULL, FALSE);
+
+  if(!g_robot_is_alive(robot))
+    return FALSE;
+
+  gboolean retval = FALSE;
+  const gint food_energy_gain = 15;
+  const gint baddie_shield_cost = 10;
+  const gint prize_score_gain = 1;
+
+  /* determine changes to x,y */
+  gint x_to, y_to, x, y, orientation;
+  map_find_thing(robot->priv->map, robot, &x, &y, &orientation);
+
+  map_compute_facing_coords(x, y, orientation, &x_to, &y_to);
+
+  gint energy_delta = - action_energy_cost[ACTION_GRAB];
+  gint shields_delta = 0;
+  gint score_delta = 0;
+
+  switch (map_get_loc_thing_char(robot->priv->map, x_to, y_to))
+    {
+    case SPACE:
+    case WALL:
+    case ROBOT:
+      retval = FALSE;
+      break;
+    case BADDIE:
+      shields_delta = - baddie_shield_cost;
+      retval = FALSE;
+      break;
+    case FOOD:
+      /* I want the net gain to be +10 */
+      energy_delta = energy_delta + food_energy_gain;
+      retval = TRUE;
+      break;
+    case PRIZE:
+      score_delta = score_delta + prize_score_gain;
+      retval = TRUE;
+      break;
+    }
+
+  /* only successful grabs get here */
+  if(retval)
+    map_remove_thing(robot->priv->map, x_to, y_to);
+
+  g_robot_stats_update(robot, "robot-grab", 6,
+		       ARG_ENERGY, robot->priv->stats.energy + energy_delta,
+		       ARG_SHIELDS, robot->priv->stats.shields + shields_delta,
+		       ARG_SCORE, robot->priv->stats.score + score_delta);
+
+  return retval;
+}
 
-	return TRUE;				/* never gets here */
+gboolean
+g_robot_zap(GRobot *robot)
+{
+  g_return_val_if_fail(G_IS_ROBOT(robot), FALSE);
+  g_return_val_if_fail(robot->priv->map != NULL, FALSE);
+
+  if(!g_robot_is_alive(robot))
+    return FALSE;
+
+  gboolean retval = FALSE;
+
+  /* determine changes to x,y */
+  gint x_to, y_to, x, y, orientation;
+  map_find_thing(robot->priv->map, robot, &x, &y, &orientation);
+
+  map_compute_facing_coords(x, y, orientation, &x_to, &y_to);
+
+  /* Did we destroy it? */
+  switch (map_get_loc_thing_char(robot->priv->map, x_to, y_to))
+    {
+    case SPACE:
+    case WALL:
+    case ROBOT:				/* what to w/ robots? */
+      retval = FALSE;
+      break;
+    case BADDIE:
+    case FOOD:
+    case PRIZE:
+      retval = map_remove_thing(robot->priv->map, x_to, y_to);
+      break;
+    }
+
+  g_robot_stats_update(robot, "robot-zap", 4,
+		       ARG_ENERGY, robot->priv->stats.energy - action_energy_cost[ACTION_ZAP],
+		       ARG_SHOTS, robot->priv->stats.shots + 1);
+
+  return retval;
 }
 
-static gint what_thing(const gchar *th)
+gboolean
+g_robot_stop(GRobot *robot)
 {
-	/* what_thing - this function scans the list of possible things
-	   (strings) and returns a cthing.  Returns -1 if not found in the
-	   list. */
+  g_signal_emit(robot, g_robot_signals[DEATH], 0);
 
-	/* My idea here is that by return -1 on error, this won't match
-	   anything in the list of cthings.  That way, the function that
-	   uses what_thing to determine the cthing doesn't have to care if
-	   the call failed or not.  This helps me keep the code simple,
-	   since now I don't have to add a branch for failure, but which
-	   also decrements energy. */
+  return TRUE;
+}
 
+/** Scans the list of possible things (strings) and returns a cthing
+ *   (location character).
+ *
+ * My idea here is that by return -1 on error, this won't match
+ * anything in the list of cthings.  That way, the function that uses
+ * what_thing to determine the cthing doesn't have to care if the call
+ * failed or not.  This helps me keep the code simple, since now I
+ * don't have to add a branch for failure, but which also decrements
+ * energy.
+ *
+ * \param[in] th string representing the location type.
+ * \sa g_robot_feel g_robot_smell g_robot_look
+ *
+ * \returns -1 if not found in the list.
+ */
+static gint
+what_thing(const gchar *th)
+{
 	gint i;
 
 	for (i = 0; i < 6; i++)
diff --git a/src/main.c b/src/main.c
index f12b3e9..bad4e0b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -18,496 +18,294 @@
  * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <stdio.h>
-#include <unistd.h>			/* for getopt */
-#include <string.h>			/* for strdup */
-
+#include <gio/gio.h>
 #include <glib.h>
 #include <glib/gprintf.h>
 
-#include <libguile.h>
-
-#include <getopt.h>			/* for GNU getopt_long */
-
-#include "grobot.h"			/* the robot structure, and robot manipulation
-								   routines */
-
-#include "api.h"			/* robot API, as Scheme functions */
-#include "ui-cmdwin.h"
-#include "ui-arena.h"
-#include "ui-window.h"
-#include "ui-window-private.h"
 #include "config.h"
 #include "configs.h"
-#include "map.h"			/* Game Map */
-#include "main.h"			/* for this source file */
-
-#define BUFF_LEN 1024
-
-/* Globals (share with api.c) */
-GList *robots = NULL;
-GRobot *robot = NULL;		/* The current robot */
-UIWindow *ui;
-UIArena *arena;
-Map *map = NULL;
-
-gpointer callback_scm(gpointer data);
-void gui_init();
-void gui_main();
-SCM catch_handler(void *data, SCM tag, SCM throw_args);
-gint is_file_readable(const gchar * filename);
-
-/************************************************************************
- * gint main(gint argc, gchar *argv[])                                  *
- *                                                                      *
- * Entry point                                                          *
- ************************************************************************/
-gint main(gint argc, gchar *argv[])
-{
-	gint opt;				/* the option read from getopt */
-	gint flag;				/* flag passed back from getopt - NOT USED */
-
-	gchar maps_path[MAX_PATH], scripts_path[MAX_PATH];
-
-	static
-	gchar *main_argv[5] = { "GNU Robots",
-		NULL,
-		NULL,
-		NULL,
-		NULL
-	};
-
-	struct option long_opts[] = {
-		{"version", 0, NULL, 'V'},
-		{"help", 0, NULL, 'h'},
-		{"map-file", 1, NULL, 'f'},
-		{"shields", 1, NULL, 's'},
-		{"energy", 1, NULL, 'e'},
-		{NULL, 0, NULL, 0}
-	};
-
-	/* Check command line */
-
-	/* Create a robot Object */
-	robot = g_robot_new(1, 1, EAST, 0, DEFAULT_ENERGY, DEFAULT_SHIELDS, 0, 0,
-		NULL, NULL);
-
-	g_assert(robot != NULL);
-
-	/* And add to to the list of robots */
-	robots = g_list_append(robots, robot);
-
-	while ((opt = getopt_long(argc, argv, "Vhf:s:e:p:", long_opts,
-				&flag)) != EOF)
-	{
-		switch (opt)
-		{
-		case 'V':
-			/* Display version, then quit */
-			g_printf("%s\n\n", PKGINFO);
-			g_printf("%s\n\n", COPYRIGHT);
-			g_printf
-("GNU Robots is free software: you can redistribute it and/or modify\n"
- "it under the terms of the GNU General Public License as published by\n"
- "the Free Software Foundation, either version 3 of the License, or\n"
- "(at your option) any later version.\n");
-			g_printf
-("\nGNU Robots is distributed in the hope that it will be useful,\n"
- "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
- "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
- "GNU General Public License for more details.\n");
-			g_printf
-("\nYou should have received a copy of the GNU General Public License\n"
- "along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.\n");
-
-			exit(0);
-			break;
-
-		case 'h':
-			/* Display help, then quit. */
-			usage(argv[0]);
-			exit(0);
-			break;
-
-		case 'f':
-			/* Set map file */
-			main_argv[1] = optarg;	/* pointer assignment */
-			break;
-
-		case 's':
-			/* Set shields */
-			robot->shields = (glong) atol(optarg);
-			break;
-
-		case 'e':
-			/* Set energy */
-			robot->energy = (glong) atol(optarg);
-			break;
-
-		default:
-			/* invalid option */
-			usage(argv[0]);
-			exit(1);
-			break;
-		}						/* switch */
-	}							/* while */
-
-	/* Extra arg is the Scheme file */
-
-	if (optind < argc)
-	{
-		/* Set Scheme file */
-		main_argv[2] = argv[optind];	/* pointer assignment */
-	}
-
-	/* Check that files have been given */
-	if (main_argv[1] == NULL)
-	{
-		main_argv[1] = g_malloc(MAX_PATH);
-		g_strlcpy(main_argv[1], DEFAULT_MAP, MAX_PATH);
-		g_fprintf(stderr, "map file not specified, trying default: %s\n",
-			main_argv[1]);
-	}
-
-	/* Check that files exist */
-	g_strlcpy(maps_path, main_argv[1], MAX_PATH);
-
-	if (!is_file_readable(maps_path))
-	{
-		g_strlcpy(maps_path, MAPS_PATH, MAX_PATH);
-		g_strlcat(maps_path, "/", MAX_PATH);
-		g_strlcat(maps_path, main_argv[1], MAX_PATH);
-
-		if (!is_file_readable(maps_path))
-		{
-			gchar *env = getenv(MAPS_PATH_ENV);
-
-			if (env != NULL)
-			{
-				g_strlcpy(maps_path, env, MAX_PATH);
-				g_strlcat(maps_path, "/", MAX_PATH);
-				g_strlcat(maps_path, main_argv[1], MAX_PATH);
-
-				if (!is_file_readable(maps_path))
-				{
-					g_fprintf(stderr,
-				"%s: %s: game map file does not exist or is not readable\n",
-						argv[0], main_argv[1]);
-					exit(1);
-				}
-			}
-			else
-			{
-				g_fprintf(stderr,
-				"%s: %s: game map file does not exist or is not readable\n",
-					argv[0], main_argv[1]);
-				exit(1);
-			}
-		}
-	}
-
-	main_argv[1] = maps_path;
-
-	/* Now the Scheme file */
-	if (main_argv[2] != NULL)
-	{
-		g_strlcpy(scripts_path, main_argv[2], MAX_PATH);
-
-		if (!is_file_readable(scripts_path))
-		{
-			g_strlcpy(scripts_path, SCRIPTS_PATH, MAX_PATH);
-			g_strlcat(scripts_path, "/", MAX_PATH);
-			g_strlcat(scripts_path, main_argv[2], MAX_PATH);
-
-			if (!is_file_readable(scripts_path))
-			{
-				gchar *env = getenv(SCRIPTS_PATH_ENV);
-
-				if (env != NULL)
-				{
-					g_strlcpy(scripts_path, env, MAX_PATH);
-					g_strlcat(scripts_path, "/", MAX_PATH);
-					g_strlcat(scripts_path, main_argv[2], MAX_PATH);
-
-					if (!is_file_readable(scripts_path))
-					{
-						g_fprintf(stderr,
-				"%s: %s: Scheme file does not exist or is not readable\n",
-							argv[0], main_argv[2]);
-						exit(1);
-					}
-				}
-				else
-				{
-					g_fprintf(stderr,
-				"%s: %s: Scheme file does not exist or is not readable\n",
-						argv[0], main_argv[2]);
-					exit(1);
-				}
-			}
-		}
-
-		main_argv[2] = scripts_path;
-	}
-	else
-	{
-		/* argv[2] can't be NULL as argv[3] may also be NULL */
-		main_argv[2] = "";
-	}
-
-	gui_init();
-
-	g_thread_unref(g_thread_new(NULL, callback_scm, main_argv));
-
-	gui_main();
-
-	return 0;				/* never gets here, but keeps compiler happy */
-}
-
-gpointer callback_scm(gpointer data)
-{
-	/* Start Guile environment.  Does not exit */
-	char **main_argv = data;
-
-	g_printf("%s\n", PKGINFO);
-	g_printf("%s\n", COPYRIGHT);
-	g_printf("GNU Robots comes with ABSOLUTELY NO WARRANTY\n");
-	g_printf
-		("This is free software, and you are welcome to redistribute it\n");
-	g_printf
-		("under certain conditions; see the file `COPYING' for details.\n");
-	g_printf("Loading Guile ... Please wait\n\n");
-
-	scm_boot_guile(3, main_argv, main_prog, NULL);
-
-	return NULL; /* never gets here, but keeps compiler happy */
-}
+#include "grobot.h"     /* the robot structure, and robot manipulation routines */
+#include "api.h"	/* robot API, as Scheme functions */
+#include "map.h"	/* Game Map */
+#include "ui-window.h"
 
-/************************************************************************
- * void main_prog(void *closure, gint argc, gchar *argv[])              *
- *                                                                      *
- * The main program code that is executed after Guile starts up. Pass   *
- * the Scheme program as argv[1] and the map file as argv[2]. The       *
- * program name is still argv[0].                                       *
- ************************************************************************/
-void main_prog(void *closure, gint argc, gchar *argv[])
+/** Function to check whether a file exists or exists in in a given
+ * path.
+ *
+ * The idea is to change given_file to a form that can be readily used
+ * in an open operation by appending path information.
+ *
+ * This function does not check file type or file readability; only
+ * existence.
+ *
+ * \param[in, out] given_file expects the file name to search for. If
+ *                 the file is found, returns TRUE, if not, it looks
+ *                 for it in path1 and then path2 and updates
+ *                 given_file accordingly if it is found.
+ * \param[in] path path to search for the file or NULL if no
+ *                 search is desired for path.
+ *
+ * \returns TRUE if the file is found; FALSE otherwise.
+ */
+static gboolean
+try_file_path(gchar *given_file, gchar *path)
 {
-	gchar *map_file = argv[1];
-	gchar *robot_program = argv[2];
-
-	api_init();
-
-	g_printf("Map file: %s\n", map_file);
-
-	map = map_new_from_file(map_file, DEFAULT_MAP_ROWS,
-		DEFAULT_MAP_COLUMNS);
-
-	if (map == NULL)
-	{
-		exit (1);
-	}
-
-	/* ensure the robot is placed properly */
-	MAP_SET_OBJECT(map, G_ROBOT_POSITION_Y(robot),
-		G_ROBOT_POSITION_X(robot), ROBOT);
-
-	/* Now initialize the rest of the Robot properties */
-	g_object_set(G_OBJECT(robot),
-		"user-interface", G_OBJECT(arena), "map", G_OBJECT(map), NULL);
-
-	g_signal_connect(G_OBJECT(robot), "death", G_CALLBACK(death), NULL);
-
-	if (strlen(robot_program) != 0)
+  if (NULL == path)
+    {
+      GFile *file = g_file_new_for_path(given_file);
+      gboolean retval = g_file_query_exists(file, NULL);
+      g_object_unref(file);
+      return retval;
+    }
+  else
+    {
+      gchar *check_path = g_build_filename(path, given_file, NULL);
+
+      if (try_file_path(check_path, NULL))
 	{
-		/* execute a Scheme file */
-		g_printf("Robot program: %s\n", robot_program);
-		scm_c_primitive_load(robot_program);
+	  g_strlcpy(given_file, check_path, MAX_PATH);
+	  g_free(check_path);
+	  return TRUE;
 	}
-	else
+      else
 	{
-		g_printf
-			("Robot program not specified. Entering interactive mode..\n");
-
-		scm_internal_catch(SCM_BOOL_T,
-		                   (scm_t_catch_body) scm_c_use_module,
-		                   "ice-9 readline",
-		                   catch_handler, NULL);
-		scm_internal_catch(SCM_BOOL_T,
-		                   (scm_t_catch_body) scm_c_eval_string,
-		                   "(activate-readline)",
-		                   catch_handler, NULL);
-		scm_internal_catch(SCM_BOOL_T,
-		                   (scm_t_catch_body) scm_c_use_module,
-		                   "system repl repl",
-		                   catch_handler, NULL);
-		scm_internal_catch(SCM_BOOL_T,
-		                   (scm_t_catch_body) scm_c_eval_string,
-		                   "(start-repl)",
-		                   catch_handler, NULL);
+	  g_free(check_path);
+	  return FALSE;
 	}
-
-	/* done */
-	exit_nicely();
+    }
 }
 
-void gui_init()
+/** Function to check whether a file exists or exists in in given
+ * paths.
+ *
+ * The idea is to change given_file to a form that can be readily used
+ * in an open operation by appending path information.
+ *
+ * \sa try_file_path
+ *
+ * \param[in, out] given_file expects the file name to search for. If
+ *                 the file is found, returns TRUE, if not, it looks
+ *                 for it in path1 and then path2 and updates
+ *                 given_file accordingly if it is found.
+ * \param[in] path1 first path to search for the file or NULL if no
+ *                 search is desired for path1.
+ * \param[in] path2 second path to search for the file or NULL if no
+ *                 search is desired for path2.
+ *
+ * \returns TRUE if the file is found; FALSE otherwise.
+ */
+static gboolean
+try_file_paths(gchar *given_file, gchar *path1, gchar *path2)
 {
-	g_printf("Loading GTK Interface ... Please wait\n\n");
-
-	gdk_threads_init();
-	gdk_threads_enter();
-	gtk_init(0, NULL);
-
-	ui = UI_WINDOW(ui_window_new());
-	arena = UI_ARENA(ui->priv->arena);
-	gdk_threads_leave();
+  if (try_file_path(given_file, NULL))
+    return(TRUE);
+  else if (NULL != path1 && try_file_path(given_file, path1))
+    return(TRUE);
+  else if (NULL != path2 && try_file_path(given_file, path2))
+    return(TRUE);
+  else
+    return(FALSE);
 }
 
-void gui_main()
-{
-	gdk_threads_enter();
-	ui_window_postinit(ui, &map); /* waits for guile to load map... */
-
-	/* draw the map */
-	ui_arena_draw(arena);
-	ui_arena_update_status(arena, "Welcome to GNU Robots", NULL,
-	                       robot->energy, robot->score, robot->shields);
-
-	gtk_main();
-	gdk_threads_leave();
-}
+/** File-local variable to hold the energy to use to when creating the robot.
+ *
+ * DEFAULT_ENERGY is defined in configs.h.
+ */
+static gint robot_energy = DEFAULT_ENERGY;
+/** File-local variable to hold the shields to use to when creating the robot.
+ *
+ * DEFAULT_SHIELDS is defined in configs.h.
+ */
+static gint robot_shields = DEFAULT_SHIELDS;
+/** File-local variable to hold the map filename to load into the map.
+ */
+static gchar *map_filename = NULL;
+/** Flag determining whether the user is requesting version information.
+ */
+static gboolean version = FALSE;
 
-/************************************************************************
- * SCM catch_handler (void *data, SCM tag, SCM throw_args);             *
- *                                                                      *
- * Responsible for handling errors                                      *
- ************************************************************************/
-SCM catch_handler(void *data, SCM tag, SCM throw_args)
+/** Program options.
+ */
+static GOptionEntry entries[] =
 {
-
-  gchar *message = NULL;
-
-  if (scm_ilength (throw_args) > 1
-      && SCM_NFALSEP (scm_string_p (SCM_CADR (throw_args))))
-  {
-		message = scm_to_locale_string (scm_simple_format (SCM_BOOL_F,
-		                                                   SCM_CADR (throw_args),
-		                                                   SCM_CADDR (throw_args)));
-  }
-
-  else if (SCM_NFALSEP (scm_symbol_p (tag)))
-  {
-		message = scm_to_locale_string (scm_symbol_to_string (tag));
-  }
-
-/*
-  user_interface_update_status (ui, message, -1, -1, -1);
-*/
-	if (message) {
-		g_printf("Invalid Instruction: %s\n", message);
-		free (message);
-	} else {
-		g_printf("Couldn't get error message\n");
-	}
-
-	return SCM_BOOL_F;
+ { "energy", 'e', 0, G_OPTION_ARG_INT, &robot_energy, "Starting energy for the robot.", "N" },
+ { "shields", 's', 0, G_OPTION_ARG_INT, &robot_shields, "Starting shields for the robot", "N" },
+ { "map-file", 'f', 0, G_OPTION_ARG_FILENAME, &map_filename, "Map file to load on startup.", "M" },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version, "Print version information and exit.", NULL },
+ { NULL }
+};
+
+/** Prints a summary of the robot's statistics after the game ends.
+ *
+ * This function is used as a signal handler for robot-death.
+ *
+ * \param[in] robot the robot whose summary is to be printed.
+ */
+static void
+print_robot_summary(GRobot *robot)
+{
+  g_return_if_fail(G_IS_ROBOT(robot));
+
+  glong score, energy, shields, shots, units;
+
+  /* Show statistics */
+  g_object_get(G_OBJECT(robot),
+	       "shields", &shields,
+	       "energy", &energy,
+	       "units", &units,
+	       "shots", &shots,
+	       "score", &score,
+	       NULL);
+
+  g_printf("\n-----------------------STATISTICS-----------------------\n");
+  g_printf("Shields: %ld\n", (shields < 0 ? 0 : shields));
+  g_printf("Energy: %ld\n", (energy < 0 ? 0 : energy));
+  g_printf("Units walked: %ld\n", (units < 0 ? 0 : units));
+  g_printf("Shots: %ld\n", (shots < 0 ? 0 : shots));
+  g_printf("Score: %ld\n", score);
+
+  /* Show results, if any */
+  if (shields < 1)
+    {
+      g_printf("** Robot took too much damage, and died.\n");
+    }
+
+  else if (energy < 1)
+    {
+      g_printf("** Robot ran out of energy.\n");
+    }
+
+  /* We get a ref increment on a signal */
+  g_object_unref(G_OBJECT(robot));
 }
 
-void death(GRobot *robot)
+/** Prints version information to stdout and exits.
+ *
+ */
+static void
+version_info(void)
 {
-	/* We get a ref increment on a signal */
-	g_object_unref(G_OBJECT(robot));
-
-	exit_nicely();
+  /* Display version, then quit */
+  g_printf("%s\n\n", PKGINFO);
+  g_printf("%s\n\n", COPYRIGHT);
+  g_printf
+    ("GNU Robots is free software: you can redistribute it and/or modify\n"
+     "it under the terms of the GNU General Public License as published by\n"
+     "the Free Software Foundation, either version 3 of the License, or\n"
+     "(at your option) any later version.\n");
+  g_printf
+    ("\nGNU Robots is distributed in the hope that it will be useful,\n"
+     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+     "GNU General Public License for more details.\n");
+  g_printf
+    ("\nYou should have received a copy of the GNU General Public License\n"
+     "along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.\n");
+
+  exit(EXIT_SUCCESS);
 }
 
-/************************************************************************
- * void exit_nicely()                                                   *
- *                                                                      *
- * A function that allows the program to exit nicely, after freeing all *
- * memory pointers, etc.                                                *
- ************************************************************************/
-void exit_nicely()
+/** Entry point
+ *
+ * \param[in] argc the number of arguments in argv plus 1.
+ * \param[in] argv the arguments passed to the program (as strings).
+ */
+int
+main(int argc, char **argv)
 {
-	glong score, energy, shields, shots, units;
-
-	/* Show statistics */
-	g_object_get(G_OBJECT(robot),
-		"shields", &shields,
-		"energy", &energy,
-		"units", &units, "shots", &shots, "score", &score, NULL);
-
-	g_printf(
-		"\n-----------------------STATISTICS-----------------------\n");
-	g_printf("Shields: %ld\n", (shields < 0 ? 0 : shields));
-	g_printf("Energy: %ld\n", (energy < 0 ? 0 : energy));
-	g_printf("Units walked: %ld\n", (units < 0 ? 0 : units));
-	g_printf("Shots: %ld\n", (shots < 0 ? 0 : shots));
-	g_printf("Score: %ld\n", score);
-
-	/* Show results, if any */
-	if (shields < 1)
+  GError *error = NULL;
+  GOptionContext *context = NULL;
+
+  context = g_option_context_new(PKGINFO);
+
+  g_option_context_add_main_entries(context, entries, NULL);
+  g_option_context_add_group(context, ui_window_gtk_option_group());
+  if(!g_option_context_parse(context, &argc, &argv, &error))
+    {
+      g_printerr("Option parsing failed: %s\n", error->message);
+      exit(EXIT_FAILURE);
+    }
+
+  if(version)
+    version_info();
+
+  /* Check that files have been given */
+  if (map_filename == NULL)
+    {
+      map_filename = g_malloc(MAX_PATH);
+      g_strlcpy(map_filename, DEFAULT_MAP, MAX_PATH);
+      g_printerr("%s: map file not specified, trying default: %s\n",
+		 g_get_prgname(), map_filename);
+    }
+
+  /* Check that files exist */
+  if (!try_file_paths(map_filename, getenv(MAPS_PATH_ENV), MAPS_PATH))
+    {
+      g_printerr("%s: %s: game map file does not exist or is not readable\n",
+		 g_get_prgname(), map_filename);
+      g_free(map_filename);
+      map_filename = NULL;
+    }
+  else
+    {
+      g_printf("%s: Using map file: %s\n", g_get_prgname(), map_filename);
+    }
+
+  /* Now the Scheme file */
+  if (argc > 1)
+    {
+      if (!try_file_paths(argv[1], getenv(SCRIPTS_PATH_ENV), SCRIPTS_PATH))
 	{
-		g_printf("** Robot took too much damage, and died.\n");
+	  g_printerr("%s: %s: scheme file does not exist or is not readable\n",
+		     g_get_prgname(), argv[1]);
+	  exit(EXIT_FAILURE);
 	}
-
-	else if (energy < 1)
+      else
+	{
+	  g_printf("%s: Using scheme file: %s\n", g_get_prgname(), argv[1]);
+	}
+    }
+
+  /* Create the Map Object */
+  g_printf("%s: Initializing map\n", g_get_prgname());
+  Map *map = map_new_blank(DEFAULT_MAP_ROWS, DEFAULT_MAP_COLUMNS);
+  g_return_val_if_fail(G_IS_MAP(map), EXIT_FAILURE);
+
+  if(NULL != map_filename)
+    {
+      GError *error = NULL;
+      map_load_filename(map, map_filename, &error);
+      if(NULL != error)
 	{
-		g_printf("** Robot ran out of energy.\n");
+	  g_printerr("%s failed to load map file: %s\nERR: %s\n",
+		     g_get_prgname(), map_filename, error->message);
+	  g_clear_error(&error);
+	  exit(EXIT_FAILURE);
 	}
+    }
 
-	g_thread_exit(0);
-}
+  /* Create a robot Object */
+  g_printf("%s: Initializing robot\n", g_get_prgname());
+  GRobot *robot = g_robot_new(robot_energy, robot_shields, 0, 0, map);
+  g_return_val_if_fail(G_IS_ROBOT(robot), EXIT_FAILURE);
 
-/************************************************************************
- * void usage(const gchar *argv0)                                       *
- *                                                                      *
- * A function that prints the usage of GNU Robots to the user. Assume   *
- * text mode for this function. We have not initialized X Windows yet.  *
- ************************************************************************/
-void usage(const gchar *argv0)
-{
-	g_printf("%s\n", PKGINFO);
-	g_printf("%s\n", COPYRIGHT);
-	g_printf
-	("Game/diversion where you construct a program for a little robot\n");
-	g_printf
-	("then set him loose and watch him explore a world on his own.\n\n");
-
-	g_printf("Usage: %s [OPTION]... [FILE]\n\n", argv0);
-	g_printf("  -f, --map-file=FILE    Load map file\n");
-	g_printf("  -s, --shields=N        Set initial shields to N\n");
-	g_printf("  -e, --energy=N         Set initial energy to N\n");
-	g_printf
-		("  -V, --version          Output version information and exit\n");
-	g_printf("  -h, --help             Display this help and exit\n");
-	g_printf("\nNote: FILE refers to a scheme file and %s enters into \n",
-		argv0);
-	g_printf("      interactive mode if it is not specified.\n");
-
-	g_printf("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
-}
+  /* Place the Robot on the Map */
+  map_auto_place_robot(map, robot);
 
-/************************************************************************
- * gint is_file_readable (const gchar *filename)                        *
- *                                                                      *
- * Checks if a file is a readable file. We will use this function as    *
- * part of a sanity check, before we get anywhere near having to open   *
- * files. This will save on error checking later on, when we may have   *
- * already initialized another environment (Curses, X Windows, ...)     *
- ************************************************************************/
-gint is_file_readable(const gchar *filename)
-{
-	FILE *stream;
+  g_signal_connect(G_OBJECT(robot), "death",
+		   G_CALLBACK(print_robot_summary), NULL);
 
-	stream = fopen(filename, "r");
-	if (stream == NULL)
-	{
-		/* Failed */
-		return (0);
-	}
+  /* Initialize Guile/Scheme */
+  GThread *guile_thread = api_init(argc, argv, robot);
+
+  /* Initialize the GUI */
+  g_printf("Loading GTK Interface ... Please wait\n\n");
+  ui_window_main(map, robot, argc, argv);
+
+  print_robot_summary(robot);
+  g_thread_unref(guile_thread);
+  g_object_unref(map);
+  g_object_unref(robot);
 
-	/* Success */
-	fclose(stream);
-	return (1);
+  return EXIT_SUCCESS; /* never gets here, but keeps compiler happy */
 }
diff --git a/src/map.c b/src/map.c
index be3ead6..6ddef8b 100644
--- a/src/map.c
+++ b/src/map.c
@@ -17,247 +17,819 @@
  * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <unistd.h>     /* for getopt */
-#include <stdio.h>
-#include <string.h>     /* for strdup */
-
+#include <gio/gio.h>
 #include <glib.h>
 
 #include "configs.h"
 #include "map.h"
 
-G_DEFINE_TYPE(Map, map, G_TYPE_OBJECT)
+struct _MapPrivate
+{
+  MapLoc 	**_map;
+  gint          max_x;
+  gint          max_y;
+  gboolean      mass_update;
+  gchar         *map_filename;
+  GSList        *robot_starting_locs;
+};
+
+enum map_properties
+  {
+   MAP_PROP_ZERO,
+   MAP_PROP_MAX_X,
+   MAP_PROP_MAX_Y,
+   MAP_PROP_LAST_PROP
+  };
+
+G_DEFINE_TYPE(Map, map, G_TYPE_OBJECT);
 
 enum
 {
-  ARG_0,
-  ARG_SIZE
+ MAP_UPDATE,
+ MAP_MOVE,
+ LAST_SIGNAL
 };
 
-static void map_set_property (GObject *object, guint prop_id,
-                  const GValue *value, GParamSpec *pspec);
+static guint map_signals[LAST_SIGNAL] = {0};
+static gboolean map_update_signal_set = FALSE;
+
+struct _MapThing
+{
+  gchar    thing_type;
+  gint     thing_orientation;
+  gpointer thing;
+};
+typedef struct _MapThing MapThing;
 
-static void map_get_property (GObject *object, guint prop_id,
-                  GValue *value, GParamSpec *pspec);
+struct _MapLoc
+{
+  gboolean  is_wall;
+  MapThing* thing;
+};
+typedef struct _MapLoc MapLoc;
 
 static void
-map_class_init (MapClass *klass)
+map_constructed (GObject *obj)
 {
-  GObjectClass *gobject_class;
+  /* update the object state depending on constructor properties */
 
-  gobject_class = G_OBJECT_CLASS(klass);
+  /* Always chain up to the parent constructed function to complete object
+   * initialisation. */
+  G_OBJECT_CLASS (map_parent_class)->constructed (obj);
+}
 
-  gobject_class->set_property = map_set_property;
-  gobject_class->get_property = map_get_property;
+static void
+map_finalize (GObject *obj)
+{
+  Map *self = MAP (obj);
+
+  g_free (self->priv->map_filename);
 
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIZE,
-                   g_param_spec_pointer ("size",
-                             "Size",
-                             "The size of the map",
-                             G_PARAM_READWRITE |
-                             G_PARAM_CONSTRUCT));
+  /* Free the map if any */
+  if (self->priv->_map != NULL)
+    {
+      for (guint x = 0; x < map_get_max_x(self); x++)
+	{
+	  g_free (self->priv->_map[x]);
+	}
+      g_free (self->priv->_map);
+    }
+
+  g_slist_free_full(self->priv->robot_starting_locs, g_free);
+  self->priv->robot_starting_locs = NULL;
+  g_free(self->priv);
+  /* Always chain up to the parent finalize function to complete object
+   * destruction. */
+  G_OBJECT_CLASS (map_parent_class)->finalize (obj);
 }
 
 static void
-map_init (Map *map)
+map_set_property(GObject *object, guint prop_id, const GValue *value,
+	GParamSpec *pspec)
 {
-  map->_map = NULL;
-  map->size.num_rows = -1;
-  map->size.num_cols = -1;
+  Map *map;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(G_IS_MAP(object));
+
+  map = MAP(object);
+
+  switch (prop_id)
+    {
+    case MAP_PROP_MAX_X:
+      map->priv->max_x = g_value_get_int(value);
+      break;
+    case MAP_PROP_MAX_Y:
+      map->priv->max_y = g_value_get_int(value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+      break;
+    }
 }
 
-/* fload_map - loads a map file into memory. */
 static void
-fload_map (Map *map, FILE *stream)
+map_get_property(GObject *object, guint prop_id, GValue *value,
+	GParamSpec *pspec)
 {
-  gint ch;
-  gint i, j;
+  Map *map;
 
-  /* Read the map file */
-  i = 0;
-  j = 0;
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(G_IS_MAP(object));
 
-  while ((ch = fgetc (stream)) != EOF)
-  {
-    /* Add the ch to the map */
-    switch (ch)
-    {
-    case SPACE:
-    case WALL:
-    case BADDIE:
-    case PRIZE:
-    case FOOD:
-      map->_map[j][i] = ch;
-      break;
+  map = MAP(object);
 
-    case '\n':
-      /* ignore the newline */
+  switch (prop_id)
+    {
+    case MAP_PROP_MAX_X:
+      g_value_set_int(value, map->priv->max_x);
+      break;
+    case MAP_PROP_MAX_Y:
+      g_value_set_int(value, map->priv->max_y);
       break;
-
     default:
-      /* not a valid map char, but we'll add a space anyway */
-      map->_map[j][i] = SPACE;
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
       break;
-    }               /* switch */
+    }
+}
+
+void
+map_class_init (MapClass *klass)
+{
+  GObjectClass *gobject_class;
 
-    /* Check for newline */
-    if (ch == '\n')
+  gobject_class = G_OBJECT_CLASS(klass);
+  gobject_class->constructed = map_constructed;
+  gobject_class->finalize = map_finalize;
+  gobject_class->set_property = map_set_property;
+  gobject_class->get_property = map_get_property;
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), MAP_PROP_MAX_X,
+     g_param_spec_int("max_x",
+		      "Maximum X",
+		      "Maximum X-value for the map.",
+		      0, G_MAXINT, DEFAULT_MAP_COLUMNS,
+		      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), MAP_PROP_MAX_Y,
+     g_param_spec_int("max_y",
+		      "Maximum Y",
+		      "Maximum Y-value for the map.",
+		      0, G_MAXINT, DEFAULT_MAP_ROWS,
+		      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  map_signals[MAP_UPDATE] =
+    g_signal_new("map-update",
+		 G_TYPE_MAP,
+		 G_SIGNAL_ACTION,
+		 0,
+                 NULL /* accumulator */,
+                 NULL /* accumulator data */,
+                 NULL /* C marshaller */,
+                 G_TYPE_NONE /* return_type */,
+                 0     /* n_params */,
+                 NULL  /* param_types */);
+  map_signals[MAP_MOVE] =
+    g_signal_new("map-move",
+		 G_TYPE_MAP,
+		 G_SIGNAL_ACTION,
+		 0,
+                 NULL /* accumulator */,
+                 NULL /* accumulator data */,
+                 NULL /* C marshaller */,
+                 G_TYPE_NONE /* return_type */,
+                 1     /* n_params */,
+                 G_TYPE_POINTER  /* param_types */);
+  map_update_signal_set = TRUE;
+}
+
+gboolean
+map_init_map(Map *self)
+{
+  g_return_val_if_fail(G_IS_MAP(self), FALSE);
+  g_return_val_if_fail(NULL != self, FALSE);
+
+  g_slist_free_full(self->priv->robot_starting_locs, g_free);
+  self->priv->robot_starting_locs = NULL;
+  if(NULL != self->priv->map_filename)
     {
-      /* the line may have ended early.  pad with WALL */
-      while (i < map->size.num_cols)
-      {
-        map->_map[j][i++] = WALL;
-      }
+      g_free(self->priv->map_filename);
+      self->priv->map_filename = NULL;
     }
 
-    /* if newline */
-    /* Check for limits on map */
-    if (++i >= map->size.num_cols)
+  gint max_x = map_get_max_x(self);
+  gint max_y = map_get_max_y(self);
+  if(max_x == 0 || max_y == 0) {
+    g_error("map_init_map: attempt to initialize map before setting max_x (%d) and max_y (%d).", max_x, max_y);
+    return(FALSE);
+  }
+
+  /* Free the map if any */
+  if (self->priv->_map != NULL)
     {
-      /* We have filled this line */
-      ++j;
-      i = 0;
+      for (guint x = 0; x < max_x; x++)
+	{
+	  g_free (self->priv->_map[x]);
+	}
+      g_free (self->priv->_map);
+    }
 
-      /* Flush the buffer for this line */
-      while (ch != '\n')
-      {
-        ch = fgetc (stream);
-      }
+  /* Allocate the map structure according to the new size */
+  self->priv->_map = g_malloc0 (max_x * sizeof (MapLoc **));
+
+  for (guint x = 0; x < max_x; x++)
+    {
+      self->priv->_map[x] = g_malloc0 (max_y * sizeof (MapLoc));
     }
-    /* if i */
-    if (j >= map->size.num_rows)
+
+  /* Set Reasonable Starting Values (all walls) */
+  self->priv->mass_update = TRUE;
+  for (guint x = 0; x < max_x; x++)
     {
-      /* the map is filled */
-      return;
+      for (guint y = 0; y < max_y; y++)
+	{
+	  map_set_loc(self, x, y, TRUE);
+	}
     }
-  }             /* while fgetc */
+  self->priv->mass_update = FALSE;
+  return(TRUE);
+}
 
-  /* After the loop, we are done reading the map file.  Make sure this
-     is bounded by WALL. */
-  if (i > 0)
-  {
-    ++j;
-  }
+void
+map_init (Map *self)
+{
+  self->priv = g_malloc(sizeof(MapPrivate));
+  self->priv->_map = NULL;
+  self->priv->max_x = 0;
+  self->priv->max_y = 0;
+  self->priv->mass_update = FALSE;
+  self->priv->map_filename = NULL;
+  self->priv->robot_starting_locs = NULL;
+}
 
-  for (i = 0; i < map->size.num_cols; i++)
-  {
-    map->_map[j][i] = WALL;
-  }
+gboolean
+map_remove_thing(Map *self, gint x, gint y)
+{
+  g_return_val_if_fail(G_IS_MAP(self), FALSE);
+  g_return_val_if_fail(NULL != self, FALSE);
+
+  gboolean retval = FALSE;
+  if(x >= 0 && x < map_get_max_x(self) &&
+     y >= 0 && y < map_get_max_y(self))
+    {
+      g_free(self->priv->_map[x][y].thing);
+      self->priv->_map[x][y].thing = NULL;
+      retval = TRUE;
+    }
+  else
+    {
+      g_error("Map reference out of range x: %d (max %d); y: %d (max %d)\n",
+	      x, map_get_max_x(self), y, map_get_max_y(self));
+    }
+
+  if(retval && ! self->priv->mass_update)
+    g_signal_emit(self, map_signals[MAP_UPDATE], 0 /* details */);
+  return retval;
 }
 
-static void
-cleanup_map (Map *map)
+gboolean
+map_place_thing(Map *self, gint x, gint y,
+		gint thing_type, gint orientation, gpointer thing)
 {
-  gint i, j;
+  g_return_val_if_fail(G_IS_MAP(self), FALSE);
+  g_return_val_if_fail(NULL != self, FALSE);
 
-  /* make sure there is a wall at top/bottom */
-  for (i = 0; i < map->size.num_cols; i++)
-  {
-    map->_map[0][i] = WALL;
-    map->_map[map->size.num_rows - 1][i] = WALL;
-  }
+  gboolean retval = FALSE;
+  if(x >= 0 && x < map_get_max_x(self) &&
+     y >= 0 && y < map_get_max_y(self))
+    {
+      if(self->priv->_map[x][y].thing == NULL)
+	{
+	  self->priv->_map[x][y].thing = g_malloc(sizeof(MapThing));
+	}
+      self->priv->_map[x][y].thing->thing_type = thing_type;
+      self->priv->_map[x][y].thing->thing_orientation = orientation;
+      self->priv->_map[x][y].thing->thing = thing;
+      retval = TRUE;
+    }
+  else
+    {
+      g_error("Map reference out of range x: %d (max %d); y: %d (max %d)\n",
+	      x, map_get_max_x(self), y, map_get_max_y(self));
+    }
 
-  /* make sure there is a wall at left/right */
-  for (j = 0; j < map->size.num_rows; j++)
-  {
-    map->_map[j][0] = WALL;
-    map->_map[j][map->size.num_cols - 1] = WALL;
-  }
+  if(retval && ! self->priv->mass_update)
+    g_signal_emit(self, map_signals[MAP_UPDATE], 0 /* details */);
+  return retval;
 }
 
-static void
-map_set_property (GObject *object, guint prop_id,
-          const GValue *value, GParamSpec *pspec)
+gboolean
+map_set_loc(Map *self, gint x, gint y, gboolean is_wall)
 {
-  Map *map;
-  guint i;
+  g_return_val_if_fail(G_IS_MAP(self), FALSE);
+  g_return_val_if_fail(NULL != self, FALSE);
 
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail (G_IS_MAP (object));
+  gboolean retval = TRUE;
 
-  map = MAP (object);
+  if(x >= 0 && x < map_get_max_x(self) &&
+     y >= 0 && y < map_get_max_y(self))
+    {
+      self->priv->_map[x][y].is_wall = is_wall ? TRUE : FALSE;
+    }
+  else
+    {
+      g_error("Map reference out of range x: %d (max %d); y: %d (max %d)\n",
+	      x, map_get_max_x(self), y, map_get_max_y(self));
+      retval = FALSE;
+    }
 
-  switch (prop_id)
-  {
-  case ARG_SIZE:
-    /* Free the map if any */
-    if (map->_map != NULL)
+  if(retval && ! self->priv->mass_update)
+    g_signal_emit(self, map_signals[MAP_UPDATE], 0 /* details */);
+  return(retval);
+}
+
+gboolean
+map_move(Map *self, gint origin_x, gint origin_y, gint dest_x, gint dest_y)
+{
+  g_return_val_if_fail(G_IS_MAP(self), FALSE);
+
+  if(origin_x < 0 || origin_x >= self->priv->max_x ||
+     origin_y < 0 || origin_y >= self->priv->max_y ||
+     dest_x < 0 || dest_x >= self->priv->max_x ||
+     dest_y < 0 || dest_y >= self->priv->max_y)
+    return(FALSE);
+
+  self->priv->mass_update = TRUE;
+  gboolean retval = FALSE;
+  g_free(self->priv->_map[dest_x][dest_y].thing);
+  self->priv->_map[dest_x][dest_y].thing = self->priv->_map[origin_x][origin_y].thing;
+  self->priv->_map[origin_x][origin_y].thing = NULL;
+  retval = TRUE;
+
+  if(retval)
     {
-      for (i = 0; i < map->size.num_rows; i++)
-      {
-        g_free (map->_map[i]);
-      }
+      MapMove *move = g_malloc(sizeof(MapMove));
+
+      move->thing = map_get_loc(self, dest_x, dest_y);
+      move->origin_x = origin_x;
+      move->origin_y = origin_y;
+      move->dest_x = dest_x;
+      move->dest_y = dest_y;
+      g_signal_emit(self, map_signals[MAP_MOVE], 0, move);
+      g_free(move); // Assume handlers copy move
+    }
+  self->priv->mass_update = FALSE;
+
+  return(retval);
+}
+
+gboolean
+map_get_loc(Map *self, gint x, gint y)
+{
+  g_return_val_if_fail(G_IS_MAP(self), -1);
+  g_return_val_if_fail(NULL != self, -1);
+
+  if(x >= 0 && x < map_get_max_x(self) &&
+     y >= 0 && y < map_get_max_y(self))
+    {
+      return(self->priv->_map[x][y].is_wall);
+    }
+  else
+    {
+      g_printerr("Map reference out of range x: %d (max %d); y: %d (max %d)\n",
+		 x, map_get_max_x(self), y, map_get_max_y(self));
+      exit(EXIT_FAILURE);
+    }
+}
 
-      g_free (map->_map);
+gint
+map_get_thing_orientation(Map *self, gint x, gint y)
+{
+  g_return_val_if_fail(G_IS_MAP(self), -1);
+  g_return_val_if_fail(NULL != self, -1);
+
+  if(x >= 0 && x < map_get_max_x(self) &&
+     y >= 0 && y < map_get_max_y(self))
+    {
+      if(NULL == self->priv->_map[x][y].thing)
+	{
+	  g_warning("map_get_thing_orientation: orientation requested for location that does not have anything in it (%d, %d)", x, y);
+	  return(-1);
+	}
+      else
+	{
+	  return(self->priv->_map[x][y].thing->thing_orientation);
+	}
+    }
+  else
+    {
+      g_printerr("Map reference out of range x: %d (max %d); y: %d (max %d)\n",
+		 x, map_get_max_x(self), y, map_get_max_y(self));
+      exit(EXIT_FAILURE);
     }
+}
+
+gboolean
+map_find_thing(Map *self, gpointer thing, gint* x, gint *y, gint *orientation)
+{
+  g_return_val_if_fail(G_IS_MAP(self), -1);
+  g_return_val_if_fail(NULL != self, -1);
+
+  if(NULL == thing)
+    return FALSE; // Not being asked to do anything
 
-    /* Get the new size of the map */
-    g_memmove (&map->size, g_value_get_pointer (value), sizeof (MapSize));
+  gboolean found = FALSE;
+  for(gint iter_x = 0; iter_x < map_get_max_x(self) && ! found; iter_x++)
+    {
+      for(gint iter_y = 0; iter_y < map_get_max_y(self) && ! found; iter_y++)
+	{
+	  if(NULL != self->priv->_map[iter_x][iter_y].thing)
+	    {
+	      if(thing == self->priv->_map[iter_x][iter_y].thing->thing)
+		{
+		  found = TRUE;
+		  if(NULL != x)
+		    *x = iter_x;
+		  if(NULL != y)
+		    *y = iter_y;
+		  if(NULL != orientation)
+		    *orientation =
+		      map_get_thing_orientation(self, iter_x, iter_y);
+		}
+	    }
+	}
+    }
+  return found;
+}
 
-    /* Allocate the according to the new size */
-    map->_map = g_malloc (map->size.num_rows * sizeof (gint **));
+gint
+map_compute_clockwise_turn(gint orientation, gint num_turns)
+{
+  if(orientation < 0 || orientation > 3)
+    g_warning("map_compute_clockwise_turn given an out of range orientation %d", orientation);
 
-    for (i = 0; i < map->size.num_rows; i++)
+  gint new_dir = (orientation + num_turns) % 4;
+  if (new_dir < 0)
     {
-      map->_map[i] = g_malloc (map->size.num_cols * sizeof (gint));
+      /* Get a positive orientation by doing a full 4 turns clockwise */
+      new_dir = new_dir + 4;
     }
+  g_assert(new_dir >= 0 && new_dir < 4);
 
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    break;
-  }
+  return new_dir;
 }
 
+gboolean
+map_thing_turn_clockwise(Map *self, gint x, gint y, gint num_turns)
+{
+  g_return_val_if_fail(G_IS_MAP(self), FALSE);
+
+  gint orientation = map_get_thing_orientation(self, x, y);
+
+  if(-1 == orientation)
+    return FALSE; // Did not find anything to rotate in this location.
+
+  if(num_turns == 0)
+    return TRUE;
+
+  self->priv->_map[x][y].thing->thing_orientation = map_compute_clockwise_turn(orientation, num_turns);
+
+  // TODO: mod the update signal to say what got update to reduce map redraws
+  g_signal_emit(self, map_signals[MAP_UPDATE], 0 /* details */);
+
+  return TRUE;
+}
+
+gchar
+map_get_loc_thing_char(Map *self, gint x, gint y)
+{
+  g_return_val_if_fail(G_IS_MAP(self), -1);
+  g_return_val_if_fail(NULL != self, -1);
+
+  if(x >= 0 && x < map_get_max_x(self) &&
+     y >= 0 && y < map_get_max_y(self))
+    {
+      if(NULL == self->priv->_map[x][y].thing)
+	{
+	  if(map_get_loc(self, x, y))
+	    return(WALL);
+	  else
+	    return(SPACE);
+	}
+      else
+	{
+	  return(self->priv->_map[x][y].thing->thing_type);
+	}
+    }
+  else
+    {
+      g_error("Map reference out of range x: %d (max %d); y: %d (max %d)\n",
+		 x, map_get_max_x(self), y, map_get_max_y(self));
+      return -1;
+    }
+}
+
+/* fload_map - loads a map file into memory. */
 static void
-map_get_property (GObject *object, guint prop_id,
-          GValue *value, GParamSpec *pspec)
+map_load_stream (Map *self, GFileInputStream *stream, GError **error)
 {
-  Map *map;
+  g_return_if_fail(G_IS_MAP (self));
 
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail (G_IS_MAP (object));
+  gint row = 0, col = 0;
 
-  map = MAP (object);
+  gint max_x = map_get_max_x(self);
+  gint max_y = map_get_max_y(self);
 
-  switch (prop_id)
+  self->priv->mass_update = TRUE;
+  /* Read the map file */
+  gssize bytesread;
+  gchar buffer[1] = "\0";
+  GError *inner_error = NULL;
+  while ((bytesread = g_input_stream_read (G_INPUT_STREAM(stream), buffer, 1, NULL, &inner_error)) > 0)
   {
-  case ARG_SIZE:
-    g_value_set_pointer (value, g_memdup (&map->size, sizeof (MapSize)));
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    break;
-  }
+    if(NULL != inner_error)
+      {
+	g_error("map_load_stream: Failure to read from GFileInputStream.");
+	g_propagate_error(error, inner_error);
+	break;
+      }
+    else if(buffer[0] == '\n')
+      {
+	++row;
+	col = 0;
+      }
+    else if(col >= max_x)
+      {
+	// flush data past the max col size of the map
+      }
+    else
+      {
+	switch(buffer[0])
+	  {
+	  case SPACE:
+	    map_set_loc(self, col, row, FALSE);
+	    map_remove_thing(self, col, row);
+	    break;
+	  case WALL:
+	    map_set_loc(self, col, row, TRUE);
+	    map_remove_thing(self, col, row);
+	    break;
+	  case BADDIE:
+	  case PRIZE:
+	  case FOOD:
+	    map_set_loc(self, col, row, FALSE);
+	    map_place_thing(self, col, row, buffer[0], NORTH, NULL);
+	    break;
+	  case ROBOT:
+	    map_set_loc(self, col, row, FALSE);
+	    /* Add to a list to be used by map_auto_place_robot later */
+	    MapCoords *coords = g_malloc(sizeof(MapCoords));
+	    coords->x = col;
+	    coords->y = row;
+	    self->priv->robot_starting_locs =
+	      g_slist_append(self->priv->robot_starting_locs, coords);
+	    break;
+	  default:
+	    g_error("map_load_stream: Unknown character in file: %d", buffer[0]);
+	    map_set_loc(self, col, row, FALSE);
+	    map_remove_thing(self, col, row);
+	  }
+	++col;
+      }
+    if (row >= max_y) /* the map is filled */
+      break;
+  }             /* while fgetc */
+  self->priv->mass_update = FALSE;
+}
+
+gint
+map_get_max_x(Map *self)
+{
+  g_return_val_if_fail(G_IS_MAP (self), -1);
+
+  return(self->priv->max_x);
+}
+
+gint
+map_get_max_y(Map *self)
+{
+  g_return_val_if_fail(G_IS_MAP (self), -1);
+
+  return(self->priv->max_y);
 }
 
 Map *
-map_new_from_file (const gchar *map_file, gint num_rows, gint num_cols)
+map_new_blank (gint num_rows, gint num_cols)
 {
-  Map *map;
-  FILE *stream;
-  MapSize size;
+  if(num_rows < 1 || num_cols < 1)
+    {
+      g_error("map_new_blank: invalid values given for num_rows and/or num_cols (%d, %d)\n", num_rows, num_cols);
+      return(NULL);
+    }
+  
+  Map *map = MAP(g_object_new (map_get_type(),
+			       "max_x", num_cols,
+			       "max_y", num_rows,
+			       NULL));
+  map_init_map(map);
+
+  return(map);
+}
 
-  size.num_rows = num_rows;
-  size.num_cols = num_cols;
+gboolean
+map_load_filename (Map *self, const gchar *map_file, GError **error)
+{
+  g_return_val_if_fail(G_IS_MAP (self), FALSE);
+
+  if(map_get_max_x(self) == 0 || map_get_max_y(self) == 0) {
+    g_error("map_load_filename: attempt to load file before initializing max_x and max_y.");
+    return(FALSE);
+  }
+
+  map_init_map(self);
 
-  stream = fopen (map_file, "r");
+  GFile *file = g_file_new_for_path(map_file);
 
-  if (stream != NULL)
+  gboolean retval = FALSE;
+  GError *inner_error = NULL;
+  GFileInputStream *stream = g_file_read(file, NULL, &inner_error);
+  if (NULL == inner_error)
   {
-    /*map = MAP (g_object_new (map_get_type (), "size", &size, NULL));*/
-    map = MAP(g_object_new (map_get_type(), "size", &size, NULL));
-    fload_map (map, stream);
-    fclose (stream);
-    cleanup_map (map);
+    map_load_stream(self, stream, &inner_error);
+    g_input_stream_close (G_INPUT_STREAM(stream), NULL, NULL);
+    g_object_unref(stream);
 
-    return map;
+    if(NULL == inner_error)
+      {
+	self->priv->map_filename = g_strdup(map_file);
+	retval = TRUE;
+      }
+    else
+      {
+	g_propagate_error(error, inner_error);
+	retval = FALSE;
+      }
   }
-
-  /* else, no file to open */
-  else
+  else /* else, no file to open */
   {
-    g_warning ("Could not open map file: %s\n", map_file);
-    return NULL;
+    g_propagate_error(error, inner_error);
+    retval = FALSE;
   }
+  g_object_unref(file);
+
+  g_signal_emit(self, map_signals[MAP_UPDATE], 0 /* details */);
+  return retval;
+}
+
+gchar*
+map_get_filename(Map *self)
+{
+  g_return_val_if_fail(G_IS_MAP (self), NULL);
+
+  return(g_strdup(self->priv->map_filename));
+}
+
+/* Sequence of positive coords closest to origin given. */
+static MapCoords*
+map_iterate_pyramid(Map *self, gint x, gint y, gint max_x, gint max_y, gchar on_loc_thing_char)
+{
+  if(x > max_x && y > max_y)
+    return NULL;
+
+  if(x >= 0 && x < max_x &&
+     y >= 0 && y < max_y)
+    {
+      if(on_loc_thing_char == map_get_loc_thing_char(self, x, y))
+	{
+	  MapCoords *coords = g_malloc(sizeof(MapCoords));
+	  coords->x = x;
+	  coords->y = y;
+	  return coords;
+	}
+    }
+  if(x == 0)
+    {
+      return map_iterate_pyramid(self, y + 1, 0, max_x, max_y, on_loc_thing_char);
+    }
+  else
+    {
+      return map_iterate_pyramid(self, x - 1, y + 1, max_x, max_y, on_loc_thing_char);
+    }
+}
+
+gboolean
+map_auto_place_thing_on(Map *self, gint thing_char, gint orientation, gpointer thing, gchar on_loc_thing_char)
+{
+  g_return_val_if_fail(G_IS_MAP (self), FALSE);
+
+  MapCoords *coords = map_iterate_pyramid(self, 0, 0,
+					  map_get_max_x(self),
+					  map_get_max_y(self),
+					  on_loc_thing_char);
+
+  if(NULL != coords)
+    {
+      return map_place_thing(self, coords->x, coords->y, thing_char, orientation, thing);
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+gboolean
+map_auto_place_robot(Map *self, gpointer robot)
+{
+  g_return_val_if_fail(G_IS_MAP (self), FALSE);
+
+  if(NULL == robot)
+    return FALSE;
+
+  gboolean retval = FALSE;
+  if(NULL != self->priv->robot_starting_locs)
+    { /* There are robot starting locs available in the map */
+      MapCoords *coords = g_slist_nth_data(self->priv->robot_starting_locs, 0);
+      if(ROBOT != map_get_loc_thing_char(self, 1, 1))
+	{
+	  self->priv->robot_starting_locs =
+	    g_slist_remove(self->priv->robot_starting_locs, coords);
+	  retval = map_place_thing(self, coords->x, coords->y,
+				   ROBOT, EAST, robot);
+	  g_free(coords);
+	  return retval;
+	}
+      else
+	{ /* There's already a robot there, so remove coords from
+	     robot_starting_locs and try again */
+	  self->priv->robot_starting_locs =
+	    g_slist_remove(self->priv->robot_starting_locs, coords);
+	  g_free(coords);
+	  retval = map_auto_place_robot(self, robot);
+	}
+    }
+  else
+    {
+      gchar place_priority[] = {SPACE, PRIZE, FOOD, BADDIE, '\0'};
+      for(gint priority = 0; place_priority[priority] != '\0'; priority++)
+	{
+	  if(map_auto_place_thing_on(self, ROBOT, EAST, robot, place_priority[priority]))
+	    return TRUE;
+	}
+    }
+
+  /* If all else fails, just put it in 1, 1 as long as there isn't
+     already a robot there */
+  if(ROBOT != map_get_loc_thing_char(self, 1, 1))
+    {
+      map_set_loc(self, 1, 1, FALSE); /* Make sure 1, 1 isn't a wall */
+      return map_place_thing(self, 1, 1, ROBOT, EAST, robot);
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+gboolean
+map_compute_facing_coords(gint from_x, gint from_y, gint orientation, gint* facing_x, gint* facing_y)
+{
+  gboolean retval = FALSE;
+  if(NULL != facing_x)
+    *facing_x = -1;
+  if(NULL != facing_y)
+    *facing_y = -1;
+
+  switch (orientation)
+    {
+    case NORTH:				/* N */
+      if(NULL != facing_y)
+	*facing_y = from_y - 1;
+      if(NULL != facing_x)
+	*facing_x = from_x;
+      retval = TRUE;
+      break;
+    case EAST:					/* E */
+      if(NULL != facing_x)
+	*facing_x = from_x + 1;
+      if(NULL != facing_y)
+	*facing_y = from_y;
+      retval = TRUE;
+      break;
+    case SOUTH:				/* S */
+      if(NULL != facing_y)
+	*facing_y = from_y + 1;
+      if(NULL != facing_x)
+	*facing_x = from_x;
+      retval = TRUE;
+      break;
+    case WEST:					/* W */
+      if(NULL != facing_x)
+	*facing_x = from_x - 1;
+      if(NULL != facing_y)
+	*facing_y = from_y;
+      retval = TRUE;
+      break;
+    }
+  return retval;
 }
diff --git a/src/ui-arena.c b/src/ui-arena.c
index e2cea1e..a6329b0 100644
--- a/src/ui-arena.c
+++ b/src/ui-arena.c
@@ -18,7 +18,9 @@
 
 #include "ui-arena.h"
 
-#include "xpm/statusbar.xpm"
+#include "configs.h"
+#include "map.h"
+
 #include "xpm/space.xpm"
 #include "xpm/food.xpm"
 #include "xpm/wall.xpm"
@@ -30,468 +32,469 @@
 #include "xpm/robot_west.xpm"
 #include "xpm/robot.xpm"
 
-#include "map.h"
-#include "configs.h"
-
 #include <stdlib.h>
-
-#include <glib/gprintf.h>
-
-#include "grobot.h"
-extern GRobot *robot;
+#include <glib.h>
 
 #define TILE_SIZE	16
 
-enum
+enum conn_signals
 {
-	ARG_0,
-	ARG_MAP
+	MAP_UPDATE,
+	MAP_MOVE,
+	LAST_CONN_SIGNAL
 };
 
-struct _UIArenaPrivate
+enum tile_index
 {
-	Map *map;
-	MapSize *map_size;
-
-	GdkPixmap *buf;
-	GdkFont *font;
-
-	int width;
-	int height;
-
-	GdkPixmap *win_bufi;
-	GdkPixmap *statusbar;
-	GdkPixmap *space;
-	GdkPixmap *food;
-	GdkPixmap *wall;
-	GdkPixmap *prize;
-	GdkPixmap *baddie;
-	GdkPixmap *robotDirs[4];
-	GdkPixmap *robotPix;
+ TILE_SPACE,
+ TILE_FOOD,
+ TILE_WALL,
+ TILE_PRIZE,
+ TILE_BADDIE,
+ TILE_ROBOT_NORTH,
+ TILE_ROBOT_SOUTH,
+ TILE_ROBOT_EAST,
+ TILE_ROBOT_WEST,
+ TILE_ROBOT,
+ TILE_LAST
 };
 
-#define UI_ARENA_GET_PRIVATE(obj) \
-    G_TYPE_INSTANCE_GET_PRIVATE(obj, UI_TYPE_ARENA, UIArenaPrivate)
+static GdkPixbuf* tile_buf[TILE_LAST] = { 0 };
 
-G_DEFINE_TYPE(UIArena, ui_arena, GTK_TYPE_DRAWING_AREA)
+static char** xpm[TILE_LAST] = \
+  {space_xpm, food_xpm, wall_xpm, prize_xpm, baddie_xpm,
+   robot_north_xpm, robot_south_xpm, robot_east_xpm,
+   robot_west_xpm, robot_xpm};
 
-static gboolean ui_arena_expose(GtkWidget *widget, GdkEventExpose *event,
-	UIArena *arena);
-static gboolean ui_arena_configure(GtkWidget *widget,
-	GdkEventConfigure *event, UIArena *arena);
-static void create_image (UIArena *arena, gchar **data, GdkPixmap **image);
-static void put_tile(UIArena *arena, GdkPixmap *image, gint x, gint y);
-
-GtkWidget *ui_arena_new()
+struct _UIArenaAnimate
 {
-	return GTK_WIDGET(g_object_new(UI_TYPE_ARENA, NULL));
-}
-
-static void ui_arena_init(UIArena *arena)
-{
-	arena->priv = UI_ARENA_GET_PRIVATE(arena);
-
-	arena->priv->map = NULL;
-	arena->priv->map_size = NULL;
-	arena->priv->width = 400;
-	arena->priv->height = 200;
-	arena->priv->font = gdk_font_from_description(
-		pango_font_description_from_string("Fixed 18"));
-
-	gtk_widget_set_size_request(GTK_WIDGET(arena), arena->priv->width,
-		arena->priv->height);
-
-	g_signal_connect(G_OBJECT(arena), "expose_event",
-		G_CALLBACK(ui_arena_expose), arena);
-	g_signal_connect(G_OBJECT(arena), "configure_event",
-		G_CALLBACK(ui_arena_configure), arena);
-
-	gtk_widget_set_events(GTK_WIDGET(arena),
-		GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
-}
+  cairo_surface_t *surface;
+  UIArena *arena;
+  gint from_x;
+  gint from_y;
+  gint to_x;
+  gint to_y;
+  gint x_delta;
+  gint y_delta;
+  gint tile;
+  gint movement;
+
+  gint max_iteration;
+  gint iteration;
+};
 
-static void ui_arena_class_init(UIArenaClass *klass)
-{
-	g_type_class_add_private(G_OBJECT_CLASS(klass),
-		sizeof(UIArenaPrivate));
-}
+typedef struct _UIArenaAnimate UIArenaAnimate;
 
-static gboolean ui_arena_expose(GtkWidget *widget, GdkEventExpose *event,
-	UIArena *arena)
+struct _UIArenaPrivate
 {
-	gdk_draw_drawable(widget->window,
-		widget->style->fg_gc[GTK_WIDGET_STATE(widget)], arena->priv->buf,
-		event->area.x, event->area.y, event->area.x, event->area.y,
-		event->area.width, event->area.height);
-
-	if(arena->priv->map)
-	{
-		gdk_draw_rectangle(widget->window, widget->style->black_gc, FALSE,
-			0, 0, widget->allocation.width - 2,
-			widget->allocation.height - 1);
-	}
+  cairo_surface_t *surface;
+  GtkDrawingArea *arena;
+  GtkLabel *filelabel;
 
-	return FALSE;
-}
+  Map *map;
 
-static gboolean ui_arena_configure(GtkWidget *widget,
-	GdkEventConfigure *event, UIArena *arena)
-{
-	if(arena->priv->buf)
-		g_object_unref(arena->priv->buf);
+  int width;
+  int height;
 
-	arena->priv->buf = gdk_pixmap_new(widget->window,
-		widget->allocation.width - 1, widget->allocation.height, -1);
+  GAsyncQueue *animation_queue;
 
-	gdk_draw_rectangle(arena->priv->buf, widget->style->white_gc, TRUE,
-		0, 0, widget->allocation.width, widget->allocation.height);
+  gulong connected_signals[LAST_CONN_SIGNAL];
+};
 
-	return TRUE;
-}
+G_DEFINE_TYPE_WITH_CODE(UIArena, ui_arena, GTK_TYPE_BOX, G_ADD_PRIVATE(UIArena))
 
-void ui_arena_set_map(UIArena *arena, Map *map)
+static gboolean
+ui_arena_draw_handler(GtkWidget *widget, cairo_t *cr, UIArena *uiarena)
 {
-	arena->priv->map = map;
-
-	g_object_get(G_OBJECT(arena->priv->map), "size",
-		&arena->priv->map_size, NULL);
+  cairo_set_source_surface(cr, uiarena->priv->surface, 0, 0);
+  cairo_paint(cr);
 
-	arena->priv->width = arena->priv->map_size->num_cols * TILE_SIZE;
-	arena->priv->height = arena->priv->map_size->num_rows * TILE_SIZE + 32;
-
-	gtk_widget_set_size_request(GTK_WIDGET(arena), arena->priv->width,
-		arena->priv->height);
+  return FALSE;
 }
 
-void ui_arena_draw(UIArena *arena)
+static void
+ui_arena_init(UIArena *self)
 {
-	gint i, j;
-
-	if(!arena->priv->map)
-		return;
-
-	for (j = 0; j < arena->priv->map_size->num_rows; j++)
-	{
-		for (i = 0; i < arena->priv->map_size->num_cols; i++)
-		{
-			switch(MAP_GET_OBJECT(arena->priv->map, i, j))
-			{
-			case '\0':
-				put_tile(arena, arena->priv->wall, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			case SPACE:
-				put_tile(arena, arena->priv->space, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			case FOOD:
-				put_tile(arena, arena->priv->food, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			case PRIZE:
-				put_tile(arena, arena->priv->prize, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			case WALL:
-				put_tile(arena, arena->priv->wall, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			case BADDIE:
-				put_tile(arena, arena->priv->baddie, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			case ROBOT:
-				put_tile(arena, arena->priv->robotDirs[robot->dir], i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			default:
-				put_tile(arena, arena->priv->wall, i * TILE_SIZE,
-					j * TILE_SIZE);
-				break;
-			}
-		}
-	}
-
-	gtk_widget_queue_draw_area(GTK_WIDGET(arena), 0, 0, arena->priv->width,
-		arena->priv->height);
+  gtk_orientable_set_orientation(GTK_ORIENTABLE(self),
+				 GTK_ORIENTATION_VERTICAL);
+
+  self->priv = ui_arena_get_instance_private(self);
+
+  self->priv->map = NULL;
+  self->priv->width = TILE_SIZE * DEFAULT_MAP_COLUMNS;
+  self->priv->height = TILE_SIZE * DEFAULT_MAP_ROWS;
+
+  self->priv->animation_queue = g_async_queue_new_full(g_free);
+
+  /* Set Up the Graphical Map Display */
+  self->priv->arena = GTK_DRAWING_AREA(gtk_drawing_area_new());
+  gtk_widget_set_size_request(GTK_WIDGET(self->priv->arena),
+			      self->priv->width,
+			      self->priv->height);
+  self->priv->surface = cairo_image_surface_create
+    (CAIRO_FORMAT_ARGB32,
+     self->priv->width,
+     self->priv->height);
+
+  g_signal_connect(G_OBJECT(self->priv->arena), "draw",
+		   G_CALLBACK(ui_arena_draw_handler), self);
+
+  gtk_widget_set_events(GTK_WIDGET(self->priv->arena),
+			GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
+
+  /* Set up the Map File Name Display */
+  GtkWidget* head = gtk_grid_new();
+  gtk_grid_set_column_homogeneous(GTK_GRID(head), FALSE);
+  gtk_grid_set_column_spacing(GTK_GRID(head), 10);
+  self->priv->filelabel = GTK_LABEL(gtk_label_new("No map loaded."));
+  gtk_label_set_justify(self->priv->filelabel, GTK_JUSTIFY_LEFT);
+  gtk_label_set_selectable(self->priv->filelabel, TRUE);
+  GtkWidget* lab = GTK_WIDGET(gtk_label_new("Map"));
+  gtk_grid_attach(GTK_GRID(head), lab, 0, 0, 1, 1);
+  gtk_grid_attach(GTK_GRID(head), GTK_WIDGET(self->priv->filelabel), 1, 0, 1, 1);
+
+  /* Add the Graphical Map and File Label to UIArean */
+  gtk_box_set_homogeneous(GTK_BOX(self), FALSE);
+  gtk_box_pack_start(GTK_BOX(self), head, FALSE, TRUE, 0);
+  gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(self->priv->arena), TRUE, TRUE, 0);
+  gtk_widget_show(GTK_WIDGET(self->priv->arena));
+  gtk_widget_show(lab);
+  gtk_widget_show(GTK_WIDGET(self->priv->filelabel));
+  gtk_widget_show(head);
 }
 
-void ui_arena_postinit(UIArena *arena)
+static void
+ui_arena_class_init(UIArenaClass *klass)
 {
-	create_image(arena, statusbar_xpm, &arena->priv->statusbar);
-	create_image(arena, space_xpm, &arena->priv->space);
-	create_image(arena, food_xpm, &arena->priv->food);
-	create_image(arena, wall_xpm, &arena->priv->wall);
-	create_image(arena, prize_xpm, &arena->priv->prize);
-	create_image(arena, baddie_xpm, &arena->priv->baddie);
-	create_image(arena, robot_north_xpm, &arena->priv->robotDirs[0]);
-	create_image(arena, robot_east_xpm, &arena->priv->robotDirs[1]);
-	create_image(arena, robot_south_xpm, &arena->priv->robotDirs[2]);
-	create_image(arena, robot_west_xpm, &arena->priv->robotDirs[3]);
-	create_image(arena, robot_xpm, &arena->priv->robotPix);
+  for(gint i = 0; i < TILE_LAST; i++)
+    {
+      tile_buf[i] = gdk_pixbuf_new_from_xpm_data((const gchar **)xpm[i]);
+      if(gdk_pixbuf_get_width(tile_buf[i]) != TILE_SIZE ||
+	 gdk_pixbuf_get_height(tile_buf[i]) != TILE_SIZE)
+	g_warning("ui_arena_class_init: tile %d has width %d; height %d. Expected %d",
+		  i,
+		  gdk_pixbuf_get_width(tile_buf[i]),
+		  gdk_pixbuf_get_height(tile_buf[i]),
+		  TILE_SIZE);
+    }
 }
 
-static void create_image (UIArena *arena, gchar **data, GdkPixmap **image)
+static void
+ui_arena_draw_tile(cairo_t *cr, GdkPixbuf *tile, gint x, gint y)
 {
-	*image = gdk_pixmap_create_from_xpm_d(GDK_DRAWABLE(arena->priv->buf),
-		NULL, NULL, data);
-	if (*image == NULL)
-		g_fprintf (stderr, "Cannot create image from xpm\n");
+  gdk_cairo_set_source_pixbuf (cr, tile, x, y);
+  cairo_rectangle(cr, x, y,
+		  TILE_SIZE, TILE_SIZE);
+  cairo_fill(cr);
 }
 
-static void put_tile(UIArena *arena, GdkPixmap *image, gint x, gint y)
+Map *
+ui_arena_get_map(UIArena *self)
 {
-	gdk_draw_drawable(arena->priv->buf,
-		GTK_WIDGET(arena)->style->fg_gc[GTK_WIDGET_STATE(arena)],
-		GDK_DRAWABLE(image), 0, 0, x, y, TILE_SIZE, TILE_SIZE);
+  g_return_val_if_fail(IS_UI_ARENA(self), NULL);
+  
+  return(self->priv->map);
 }
 
-void ui_arena_update_status(UIArena *arena, const gchar *fmt, const gchar *thing,
-	glong energy, glong score, glong shields)
+static gint
+ui_arena_get_loc_tile_index(UIArena *self, gint x, gint y)
 {
-	gchar status[20];
-	gint x = 0;
-	gchar *s = NULL;
-
-	while(x < arena->priv->width)
+  gint retval = TILE_WALL;
+  switch(map_get_loc_thing_char(self->priv->map, x, y))
+    {
+    case SPACE:
+      retval = TILE_SPACE;
+      break;
+    case FOOD:
+      retval = TILE_FOOD;
+      break;
+    case PRIZE:
+      retval = TILE_PRIZE;
+      break;
+    case WALL:
+      retval = TILE_WALL;
+      break;
+    case BADDIE:
+      retval = TILE_BADDIE;
+      break;
+    case ROBOT:
+      switch(map_get_thing_orientation(self->priv->map, x, y))
 	{
-		gdk_draw_drawable(arena->priv->buf,
-			GTK_WIDGET(arena)->style->white_gc,
-			arena->priv->statusbar, 0, 0, x,
-			arena->priv->map_size->num_rows * TILE_SIZE, 96, 32);
-		x += 96;
-	}
-
-	s = g_strdup_printf(fmt, thing);
-	gdk_draw_string(arena->priv->buf, arena->priv->font,
-		GTK_WIDGET(arena)->style->white_gc, 3,
-		arena->priv->map_size->num_rows * TILE_SIZE + 16, s);
-	g_free(s);
-
-	if(energy > -1)
-	{
-		g_sprintf(status, "Robot Energy: %3ld", energy);
-		gdk_draw_string(arena->priv->buf, arena->priv->font,
-			GTK_WIDGET(arena)->style->white_gc, 240,
-			arena->priv->map_size->num_rows * TILE_SIZE + 12, status);
-	}
-
-	if (score > -1)
-	{
-		g_sprintf (status, "Robot Score: %3ld", score);
-		gdk_draw_string(arena->priv->buf, arena->priv->font,
-			GTK_WIDGET(arena)->style->white_gc, 240,
-			arena->priv->map_size->num_rows * TILE_SIZE + 25, status);
-	}
-
-	if (shields > -1)
-	{
-		g_sprintf (status, "Robot Shields: %3ld", shields);
-		gdk_draw_string(arena->priv->buf, arena->priv->font,
-			GTK_WIDGET(arena)->style->white_gc, 480,
-			arena->priv->map_size->num_rows * TILE_SIZE + 12, status);
+	case NORTH:
+	  retval = TILE_ROBOT_NORTH;
+	  break;
+	case SOUTH:
+	  retval = TILE_ROBOT_SOUTH;
+	  break;
+	case EAST:
+	  retval = TILE_ROBOT_EAST;
+	  break;
+	case WEST:
+	  retval = TILE_ROBOT_WEST;
+	  break;
+	default:
+	  retval = TILE_ROBOT;
+	  g_warning("ui_arena_get_loc_tile_index: unexpected robot dir %d",
+		    map_get_thing_orientation(self->priv->map, x, y));
 	}
-
-	gtk_widget_queue_draw_area(GTK_WIDGET(arena), 0, 0, arena->priv->width,
-		arena->priv->height);
+      break;
+    default:
+      retval = TILE_WALL;
+      g_warning("ui_arena_get_loc_tile_index: unexpected tile %d", map_get_loc_thing_char(self->priv->map, x, y));
+    }
+  return(retval);
 }
 
-/* note that hook_delete_thing(x,y) is the same as
- * hook_add_thing(x,y,space) */
-void ui_arena_add_thing(UIArena *arena, gint x, gint y, gint thing)
+void
+ui_arena_draw(UIArena *self)
 {
-	gint w_x, w_y;
+  g_return_if_fail(IS_UI_ARENA(self));
 
-	w_x = x * TILE_SIZE;
-	w_y = y * TILE_SIZE;
+  cairo_t *cr = cairo_create(self->priv->surface);
+  cairo_push_group(cr);
+  gint col, row;
 
-	gdk_threads_enter ();
-
-	switch (thing)
+  gint num_cols = map_get_max_x(self->priv->map);
+  gint num_rows = map_get_max_y(self->priv->map);
+  for (row = 0; row < num_rows; row++)
+    {
+      for (col = 0; col < num_cols; col++)
 	{
-	case SPACE:
-		put_tile(arena, arena->priv->space, w_x, w_y);
-		break;
-	case FOOD:
-		put_tile(arena, arena->priv->food, w_x, w_y);
-		break;
-	case PRIZE:
-		put_tile(arena, arena->priv->prize, w_x, w_y);
-		break;
-	case WALL:
-		put_tile(arena, arena->priv->wall, w_x, w_y);
-		break;
-	case BADDIE:
-		put_tile(arena, arena->priv->baddie, w_x, w_y);
-		break;
-	case ROBOT:
-		put_tile(arena, arena->priv->robotDirs[robot->dir], w_x, w_y);
-		break;
-	default:
-		put_tile(arena, arena->priv->wall, w_x, w_y);
-		break;
+	  if(!(0 != g_async_queue_length(self->priv->animation_queue) &&
+	       ROBOT == map_get_loc_thing_char(self->priv->map, col, row)))
+	    ui_arena_draw_tile(cr,
+			       tile_buf[ui_arena_get_loc_tile_index(self, col, row)],
+			       col * TILE_SIZE,
+			       row * TILE_SIZE);
+	  else
+	    ui_arena_draw_tile(cr,
+			       tile_buf[TILE_SPACE],
+			       col * TILE_SIZE,
+			       row * TILE_SIZE);
 	}
-
-	gtk_widget_queue_draw_area(GTK_WIDGET(arena), 0, 0, arena->priv->width,
-		arena->priv->height);
-
-	gdk_threads_leave ();
+    }
+
+  cairo_pop_group_to_source(cr);
+  cairo_paint(cr);
+
+  cairo_rectangle_int_t rect = {0, 0,
+				map_get_max_x(self->priv->map) * TILE_SIZE,
+				map_get_max_y(self->priv->map) * TILE_SIZE};
+  cairo_region_t *crect = cairo_region_create_rectangle(&rect);
+  GdkDrawingContext *gcx = gdk_window_begin_draw_frame(gtk_widget_get_window(GTK_WIDGET(self->priv->arena)), crect);
+  cairo_region_destroy(crect);
+  cairo_t *gcr = gdk_drawing_context_get_cairo_context(gcx);
+  cairo_set_source_surface(gcr, self->priv->surface, 0, 0);
+  cairo_paint(gcr);
+
+  gdk_window_end_draw_frame(gtk_widget_get_window(GTK_WIDGET(self->priv->arena)), gcx);
+  gtk_widget_queue_draw(GTK_WIDGET(self->priv->arena));
 }
 
-void ui_arena_move_robot(UIArena *arena, gint from_x, gint from_y,
-	gint to_x, gint to_y, gint cdir, glong energy, glong score,
-	glong shields)
+static gboolean
+ui_arena_animate(GAsyncQueue *animation_queue)
 {
-	const static gint movement = TILE_SIZE / 16;
-	guint8 distance = dist(from_x, from_y, to_x, to_y);
-	guint w_x = to_x * TILE_SIZE, w_y = to_y * TILE_SIZE, tw_x, tw_y;
-	gboolean ok;
-
-	g_assert(distance <= 1);
-
-	gdk_threads_enter ();
-
-	ui_arena_update_status(arena, "Robot moves..", NULL,
-	                       energy, score, shields);
-
-	/* Check if robot is moving within a single box */
-	if (distance == 0)
-	{
-		put_tile(arena, arena->priv->space, from_x * TILE_SIZE,
-			from_y * TILE_SIZE);
-		put_tile(arena, arena->priv->robotDirs[cdir], to_x * TILE_SIZE,
-			to_y * TILE_SIZE);
-
-		gtk_widget_queue_draw_area(GTK_WIDGET(arena), 0, 0,
-			arena->priv->width,	arena->priv->height);
+  UIArenaAnimate *animation = g_async_queue_pop(animation_queue);
+  if(NULL == animation)
+    return FALSE;
+
+  GtkWidget *arena = GTK_WIDGET(UI_ARENA(animation->arena)->priv->arena);
+
+  cairo_rectangle_int_t rect =
+    {(animation->from_x < animation->to_x ? animation->from_x : animation->to_x),
+     (animation->from_y < animation->to_y ? animation->from_y : animation->to_y),
+     abs(animation->from_x - animation->to_x) + TILE_SIZE,
+     abs(animation->from_y - animation->to_y) + TILE_SIZE};
+  cairo_region_t *crect = cairo_region_create_rectangle(&rect);
+  UIArena *uiarena = UI_ARENA(animation->arena);
+  cairo_t *cr = cairo_create(uiarena->priv->surface);
+
+  cairo_push_group(cr);
+  cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+  cairo_clip(cr);
+
+  ui_arena_draw_tile
+    (cr,
+     tile_buf[TILE_SPACE],
+     animation->from_x,
+     animation->from_y);
+  ui_arena_draw_tile
+    (cr,
+     tile_buf[animation->tile],
+     animation->from_x + animation->x_delta * (animation->max_iteration - animation->iteration + 1) * animation->movement,
+     animation->from_y + animation->y_delta * (animation->max_iteration - animation->iteration + 1) * animation->movement);
+
+  cairo_pop_group_to_source(cr);
+  cairo_paint(cr);
+
+  GdkDrawingContext *gcx = gdk_window_begin_draw_frame
+    (gtk_widget_get_window(arena), crect);
+  cairo_region_destroy(crect);
+  cairo_t *gcr = gdk_drawing_context_get_cairo_context(gcx);
+  g_assert(NULL != uiarena->priv->surface);
+  cairo_set_source_surface(gcr, uiarena->priv->surface, 0, 0);
+  cairo_paint(gcr);
+
+  gtk_widget_queue_draw_area(arena, rect.x, rect.y, rect.width, rect.height);
+  gdk_window_end_draw_frame(gtk_widget_get_window(arena), gcx);
+  gtk_widget_queue_draw(arena);
+
+  animation->iteration = animation->iteration - 1;
+
+  if(0 == animation->iteration)
+      g_free(animation);
+  else
+      g_async_queue_push_front(animation_queue, animation);
+
+  if(0 == g_async_queue_length(animation_queue))
+    {
+      ui_arena_draw(uiarena);
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
 
-		gdk_threads_leave ();
-		g_usleep(USLEEP_TIME);
-		return;
-	}
+static void
+ui_arena_move_robot(UIArena *self, gint from_x, gint from_y,
+	gint to_x, gint to_y)
+{
+  UIArenaAnimate *animation = g_malloc(sizeof(UIArenaAnimate));
+  animation->arena = self;
+  animation->movement = TILE_SIZE / 8;
+  animation->max_iteration = 8;
+  animation->iteration = 8;
+  animation->tile = TILE_ROBOT;
+  animation->x_delta = 0;
+  animation->y_delta = 0;
+  animation->from_x = from_x * TILE_SIZE;
+  animation->from_y = from_y * TILE_SIZE;
+  animation->to_x = to_x * TILE_SIZE;
+  animation->to_y = to_y * TILE_SIZE;
+  if(from_y - to_y > 0 && from_x - to_x == 0)
+    {
+      animation->y_delta = -1;
+      animation->tile = TILE_ROBOT_NORTH;
+    }
+  else if(from_y - to_y < 0 && from_x - to_x == 0)
+    {
+      animation->y_delta = 1;
+      animation->tile = TILE_ROBOT_SOUTH;
+    }
+  else if(from_y - to_y == 0 && from_x - to_x < 0)
+    {
+      animation->x_delta = 1;
+      animation->tile = TILE_ROBOT_EAST;
+    }
+  else if(from_y - to_y == 0 && from_x - to_x > 0)
+    {
+      animation->x_delta = -1;
+      animation->tile = TILE_ROBOT_WEST;
+    }
+  else if(from_y == to_y && from_x == to_x)
+    {
+      g_debug("ui_arena_move_robot: Called but no move requested (%d, %d)->(%d, %d).",
+	      from_x, from_y, to_x, to_y);
+    }
+  else
+    {
+      animation->tile = TILE_ROBOT;
+      g_warning("ui_arena_move_robot: Weird unknown robot direction (%d, %d)->(%d, %d). I'm Confused.",
+		from_x, from_y, to_x, to_y);
+    }
+
+  if(0 == g_async_queue_length(self->priv->animation_queue))
+    {
+      g_async_queue_push(self->priv->animation_queue, animation);
+      gdk_threads_add_timeout(SLEEP_TIME/8, G_SOURCE_FUNC(ui_arena_animate), self->priv->animation_queue);
+      
+    }
+  else
+    { /* Entries in the queue mean that there should already be an
+	 animation timeout running */
+      g_async_queue_push(self->priv->animation_queue, animation);
+    }
+}
 
-	from_x *= TILE_SIZE;
-	from_y *= TILE_SIZE;
-	tw_y = w_y;
-	tw_x = w_x;
-	switch (cdir)
-	{
-	case NORTH:
-		tw_y = from_y - movement;
-		break;
-	case SOUTH:
-		tw_y = from_y + movement;
-		break;
-	case EAST:
-		tw_x = from_x + movement;
-		break;
-	case WEST:
-		tw_x = from_x - movement;
-		break;
-	default:
-		g_printf("Weird unknown robot direction. I'm Confused.\n");
-	}
+void
+ui_arena_move_robot_marshal(gpointer data_wrapper[])
+{
+  MapMove *move = data_wrapper[1];
+  ui_arena_move_robot(data_wrapper[0],
+		      move->origin_x,
+		      move->origin_y,
+		      move->dest_x,
+		      move->dest_y);
+
+  g_free(move);
+  g_free(data_wrapper);
+}
 
+static void
+ui_arena_on_map_move(Map *map, gpointer param, gpointer data)
+{
+  g_return_if_fail(IS_UI_ARENA(data));
 
-	while (1)
-	{
-		put_tile(arena, arena->priv->space, from_x, from_y);
-		put_tile(arena, arena->priv->robotDirs[cdir], tw_x, tw_y);
-
-		ok = FALSE;
-		if (tw_x < w_x)
-		{
-			tw_x += movement;
-			ok = TRUE;
-		}
-		else if (tw_x > w_x)
-		{
-			tw_x -= movement;
-			ok = TRUE;
-		}
-		if (tw_y < w_y)
-		{
-			tw_y += movement;
-			ok = TRUE;
-		}
-		else if (tw_y > w_y)
-		{
-			tw_y -= movement;
-			ok = TRUE;
-		}
-
-		gtk_widget_queue_draw_area(GTK_WIDGET(arena), 0, 0,
-			arena->priv->width,	arena->priv->height);
-		gdk_window_process_all_updates();
-
-		gdk_threads_leave ();
-		g_usleep(USLEEP_TIME / 16);
-		gdk_threads_enter ();
-
-		if (!ok)
-			break;
-	}
+  gpointer* data_wrapper;
+  data_wrapper = g_malloc(sizeof(gpointer) * 2);
+  data_wrapper[0] = data;
+  data_wrapper[1] = g_memdup(param, sizeof(MapMove));
 
-	gdk_threads_leave ();
-	g_usleep(USLEEP_TIME);
+  gdk_threads_add_idle(G_SOURCE_FUNC(ui_arena_move_robot_marshal), data_wrapper);
 }
 
-/* hooks to animate the robot */
-void ui_arena_robot_smell(UIArena *arena, gint x, gint y, gint cdir,
-	glong energy, glong score, glong shields, const gchar *thing)
+static void
+ui_arena_map_filename_update(UIArena *self)
 {
-	gdk_threads_enter ();
-	/* If we want to change the pic, do it here */
-	ui_arena_update_status(arena, "Robot sniffs for %s...", thing,
-		energy, score, shields);
-	gdk_threads_leave ();
+  gchar* mapfile = map_get_filename(self->priv->map);
 
-	g_usleep(USLEEP_TIME);
-}
+  gtk_label_set_text(self->priv->filelabel,
+		     NULL != mapfile ? mapfile : "No map loaded.");
 
-void ui_arena_robot_zap(UIArena *arena,	gint x,	gint y,	gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields)
-{
-	gdk_threads_enter ();
-	ui_arena_update_status(arena, "Robot fires his little gun...", NULL,
-		energy, score, shields);
-	gdk_threads_leave ();
+  gtk_widget_queue_draw(GTK_WIDGET(self->priv->filelabel));
 
-	g_usleep(USLEEP_TIME);
+  g_free(mapfile);
 }
 
-void ui_arena_robot_feel(UIArena *arena,
-	gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields,
-	const gchar *thing)
+static void
+ui_arena_on_map_update(Map *map, gpointer data)
 {
-	gdk_threads_enter ();
-	ui_arena_update_status(arena, "Robot feels for %s...", thing,
-		energy, score, shields);
-	gdk_threads_leave ();
+  g_return_if_fail(IS_UI_ARENA(data));
 
-	g_usleep(USLEEP_TIME);
+  gdk_threads_add_idle(G_SOURCE_FUNC(ui_arena_draw), data);
+  gdk_threads_add_idle(G_SOURCE_FUNC(ui_arena_map_filename_update), data);
 }
 
-void ui_arena_robot_grab(UIArena *arena, gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields)
+void
+ui_arena_set_map(UIArena *arena, Map *map)
 {
-	gdk_threads_enter ();
-	ui_arena_update_status(arena, "Robot grabs...", NULL,
-		energy, score, shields);
-	gdk_threads_leave ();
-
-	g_usleep(USLEEP_TIME);
+  g_return_if_fail(IS_UI_ARENA(arena));
+  g_return_if_fail(G_IS_MAP(map));
+
+  arena->priv->map = map;
+  gint num_cols = map_get_max_x(arena->priv->map);
+  gint num_rows = map_get_max_y(arena->priv->map);
+  arena->priv->width = num_cols * TILE_SIZE;
+  arena->priv->height = num_rows * TILE_SIZE + 32;
+  ui_arena_map_filename_update(arena);
+
+  gtk_widget_set_size_request(GTK_WIDGET(arena), arena->priv->width,
+			      arena->priv->height);
+  arena->priv->connected_signals[MAP_UPDATE] = g_signal_connect(map, "map-update", G_CALLBACK(ui_arena_on_map_update), arena);
+  arena->priv->connected_signals[MAP_MOVE] = g_signal_connect(map, "map-move",   G_CALLBACK(ui_arena_on_map_move), arena);
+  ui_arena_draw(arena);
 }
 
-void ui_arena_robot_look(UIArena *arena, gint x, gint y, gint cdir,
-	gint x_to, gint y_to, glong energy, glong score, glong shields,
-	const gchar *thing)
+GtkWidget *
+ui_arena_new(Map *map)
 {
-	gdk_threads_enter ();
-	ui_arena_update_status(arena, "Robot looks for %s...", thing,
-		energy, score, shields);
-	gdk_threads_leave ();
+  UIArena* arena = UI_ARENA(g_object_new(UI_TYPE_ARENA, NULL));
+
+  if(NULL != map)
+    {
+      g_return_val_if_fail(G_IS_MAP(map), NULL);
+      ui_arena_set_map(arena, map);
+    }
 
-	g_usleep(USLEEP_TIME);
+  return GTK_WIDGET(arena);
 }
diff --git a/src/ui-cmdwin.c b/src/ui-cmdwin.c
index f663aa1..6728af4 100644
--- a/src/ui-cmdwin.c
+++ b/src/ui-cmdwin.c
@@ -28,21 +28,23 @@ struct _UICmdWinPrivate
 	GtkWidget *vte;
 };
 
-#define UI_CMDWIN_GET_PRIVATE(obj) \
-    G_TYPE_INSTANCE_GET_PRIVATE(obj, UI_TYPE_CMDWIN, UICmdWinPrivate)
-
-G_DEFINE_TYPE(UICmdWin, ui_cmdwin, GTK_TYPE_VBOX)
+G_DEFINE_TYPE_WITH_CODE(UICmdWin, ui_cmdwin, GTK_TYPE_BOX, G_ADD_PRIVATE(UICmdWin))
 
 GtkWidget *ui_cmdwin_new(void)
 {
-	return GTK_WIDGET(g_object_new(UI_TYPE_CMDWIN, NULL));
+  UICmdWin *self = g_object_new(UI_TYPE_CMDWIN, NULL);
+
+  return GTK_WIDGET(self);
 }
 
 static void ui_cmdwin_init(UICmdWin *cmdwin)
 {
+	gtk_orientable_set_orientation(GTK_ORIENTABLE(cmdwin),
+				       GTK_ORIENTATION_VERTICAL);
+
 	int master_pty, slave_pty;
 
-	cmdwin->priv = UI_CMDWIN_GET_PRIVATE(cmdwin);
+	cmdwin->priv = ui_cmdwin_get_instance_private(cmdwin);
 
 	cmdwin->priv->vte = vte_terminal_new();
 
@@ -64,13 +66,13 @@ static void ui_cmdwin_init(UICmdWin *cmdwin)
 	fflush (stdout); setlinebuf (stdout);
 	fflush (stderr); setlinebuf (stderr);
 
-	vte_terminal_set_pty(VTE_TERMINAL(cmdwin->priv->vte), master_pty);
+	VtePty *fpty = vte_pty_new_foreign_sync(master_pty, NULL, NULL);
+
+	vte_terminal_set_pty(VTE_TERMINAL(cmdwin->priv->vte), fpty);
 
 	gtk_widget_show(cmdwin->priv->vte);
 }
 
 static void ui_cmdwin_class_init(UICmdWinClass *klass)
 {
-	g_type_class_add_private(G_OBJECT_CLASS(klass),
-		sizeof(UICmdWinPrivate));
 }
diff --git a/src/ui-status.c b/src/ui-status.c
new file mode 100644
index 0000000..d9aca37
--- /dev/null
+++ b/src/ui-status.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 2020 Matt Henry <mcthenry+gnu@gmail.com>
+ *
+ * GNU Robots, ui-status.c
+ *
+ * GNU Robots is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GNU Robots is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ui-status.h"
+#include "configs.h"
+#include "grobot.h"
+
+#include <glib.h>
+
+enum conn_signals
+{
+ ROBOT_UPDATE,
+ LAST_CONN_SIGNAL
+};
+
+enum status_headings
+{
+ TURN,
+ ACTION,
+ ENERGY,
+ SHIELDS,
+ SHOTS,
+ SCORE,
+ LAST_HEADING
+};
+
+static const gchar *status_heading[LAST_HEADING] =				\
+  {"Turn", "Action", "Energy", "Shields", "Shots", "Score"};
+
+struct _UIStatusPrivate
+{
+  GRobot   *robot;
+
+  gchar    *last_status;
+  gint      status_count;
+  GtkLabel *status_labels[LAST_HEADING];
+  gulong    connected_signals[LAST_CONN_SIGNAL];
+};
+
+G_DEFINE_TYPE(UIStatus, ui_status, GTK_TYPE_GRID)
+
+GtkWidget *ui_status_new()
+{
+  return GTK_WIDGET(g_object_new(UI_TYPE_STATUS, NULL));
+}
+
+static void
+ui_status_class_init (UIStatusClass *klass)
+{
+  // TODO: Do we need to chain to the parent?
+}
+
+static void
+ui_status_init(UIStatus *self)
+{
+  self->priv = g_malloc(sizeof(UIStatusPrivate));
+  self->priv->last_status = g_strdup("");
+  self->priv->status_count = 0;
+
+  gtk_grid_set_column_homogeneous(GTK_GRID(self), TRUE);
+
+  for(gint i = 0; i < 6; i++)
+    {
+      GtkWidget *lab = gtk_label_new(status_heading[i]);
+      gtk_grid_attach(GTK_GRID(self), GTK_WIDGET(lab), i, 0, 1, 1);
+      gtk_widget_show(lab);
+    }
+}
+
+static gchar*
+ui_status_num_delta_markup(glong before_num, glong after_num)
+{
+  gchar *retval;
+  if(before_num == after_num)
+    {
+      retval = g_strdup_printf("%ld", after_num);
+    }
+  else if (before_num < after_num)
+    {
+      retval = g_markup_printf_escaped ("%ld <span font-weight=\"bold\" foreground=\"blue\">+%ld</span>",
+			       after_num,
+			       after_num - before_num);
+    }
+  else
+    {
+      retval = g_markup_printf_escaped ("%ld <span font-weight=\"bold\" foreground=\"red\">%ld</span>",
+			       after_num,
+			       after_num - before_num);
+    }
+  return retval;
+}
+
+static void
+ui_status_update_status(UIStatus *self, GRobot_change *change)
+{
+  for(gint i = 0; i < LAST_HEADING; i++)
+    {
+      gchar *value = NULL;
+      switch(i)
+	{
+	case TURN:
+	  value = g_strdup_printf("%ld", change->after_change.turn);
+	  break;
+	case ACTION:
+	  if(strcmp(change->status_message, self->priv->last_status) == 0)
+	    self->priv->status_count++;
+	  else
+	    {
+	      g_free(self->priv->last_status);
+	      self->priv->status_count = 1;
+	      self->priv->last_status = g_strdup(change->status_message);
+	    }
+
+	  if(self->priv->status_count > 1)
+	    value = g_markup_printf_escaped("%s <span font-weight=\"bold\" foreground=\"blue\">x%d</span>",
+					    change->status_message,
+					    self->priv->status_count);
+	  else
+	    value = g_strdup(change->status_message);
+	  break;
+	case SHIELDS:
+	  value = ui_status_num_delta_markup(change->before_change.shields,
+					     change->after_change.shields);
+	  break;
+	case ENERGY:
+	  value = ui_status_num_delta_markup(change->before_change.energy,
+					     change->after_change.energy);
+	  break;
+	case SCORE:
+	  value = ui_status_num_delta_markup(change->before_change.score,
+					     change->after_change.score);
+	  break;
+	case SHOTS:
+	  value = ui_status_num_delta_markup(change->before_change.shots,
+					     change->after_change.shots);
+	  break;
+	default:
+	  if(NULL != self->priv->status_labels[i])
+	    g_warning("ui_status_update_status: don't know how to update %d", i);
+	}
+      if(NULL != value && NULL != self->priv->status_labels[i])
+	{
+	  gtk_label_set_markup(self->priv->status_labels[i], value);
+	}
+      g_free(value); // Free the memory since it was never used
+    }
+}
+
+static void
+ui_status_update_status_unmarshal(gpointer data_wrapper[])
+{
+  GRobot_change *change = data_wrapper[1];
+  ui_status_update_status(UI_STATUS(data_wrapper[0]), change);
+
+  g_free(change);
+  g_free(data_wrapper);
+}
+
+static void
+ui_status_on_stats_update(GRobot *robot, gpointer param, gpointer data)
+{
+  g_return_if_fail(IS_UI_STATUS(data));
+  /* TODO: this check currently won't work because G_IS_ROBOT isn't fully defined in grobot.h
+  g_return_if_fail(G_IS_ROBOT(robot)); */
+
+  gpointer* data_wrapper;
+  data_wrapper = g_malloc(sizeof(gpointer) * 2);
+  data_wrapper[0] = data;
+  data_wrapper[1] = g_memdup(param, sizeof(GRobot_change));
+
+  gdk_threads_add_idle(G_SOURCE_FUNC(ui_status_update_status_unmarshal), data_wrapper);
+}
+
+void
+ui_status_set_robot(UIStatus *self, GRobot *robot)
+{
+  g_return_if_fail(IS_UI_STATUS(self));
+  g_return_if_fail(G_IS_ROBOT(robot));
+
+  self->priv->robot = g_object_ref(robot);
+
+  for(gint i = 0; i < LAST_HEADING; i++)
+    {
+      gchar *value = NULL;
+      switch(i)
+	{
+	case TURN:
+	  value = g_strdup_printf("%ld", g_robot_get_turn(robot));
+	  break;
+	case ACTION:
+	  value = "Startup";
+	  break;
+	case SHIELDS:
+	  value = g_strdup_printf("%ld", g_robot_get_shields(robot));
+	  break;
+	case ENERGY:
+	  value = g_strdup_printf("%ld", g_robot_get_energy(robot));
+	  break;
+	case SCORE:
+	  value = g_strdup_printf("%ld", g_robot_get_score(robot));
+	  break;
+	case SHOTS:
+	  value = g_strdup_printf("%ld", g_robot_get_shots(robot));
+	  break;
+	  /* default: */
+	  /* Don't add in the default case. */
+	}
+      if(NULL != value)
+	{
+	  GtkLabel *lab = GTK_LABEL(gtk_label_new(value));
+	  gtk_label_set_use_markup(lab, TRUE);
+	  gtk_grid_attach(GTK_GRID(self), GTK_WIDGET(lab), i, 1, 1, 1);
+	  gtk_widget_show(GTK_WIDGET(lab));
+	  self->priv->status_labels[i] = lab; /* Hang on the labels so that status
+				     update can use them. */
+	}
+    }
+
+  self->priv->connected_signals[ROBOT_UPDATE] =
+    g_signal_connect(robot,
+		     "robot-stats-update",
+		     G_CALLBACK(ui_status_on_stats_update),
+		     self);
+}
+
+GRobot*
+ui_status_get_robot(UIStatus *self)
+{
+  g_return_val_if_fail(IS_UI_STATUS(self), NULL);
+
+  return(self->priv->robot);
+}
diff --git a/src/ui-window.c b/src/ui-window.c
index 8034138..dd976c9 100644
--- a/src/ui-window.c
+++ b/src/ui-window.c
@@ -17,80 +17,443 @@
  */
 
 #include "ui-window.h"
-#include "ui-window-private.h"
-
 #include "ui-cmdwin.h"
 #include "ui-arena.h"
+#include "ui-status.h"
 
-#include "main.h"
+#include "grobot.h"
 #include "map.h"
+#include "api.h"
+
+#include "config.h"
 #include "configs.h"
 
-#include <gtk/gtkmain.h>
+#include "xpm/robot.xpm"
+
+#include <gtk/gtk.h>
+
+struct _UIWindowPrivate {
+  GtkWidget *menubar;
+  GtkWidget *statustab;
+  GtkWidget *cmdwin;
+  GtkWidget *arena;
+
+  Map *map;
+  GRobot *robot;
+};
+
+enum ui_window_properties
+  {
+   UI_WINDOW_PROP_0,
+   UI_WINDOW_PROP_MAP,
+   UI_WINDOW_PROP_ROBOT,
+   UI_WINDOW_PROP_LAST
+  };
+
+G_DEFINE_TYPE_WITH_PRIVATE (UIWindow, ui_window, GTK_TYPE_APPLICATION_WINDOW)
+
+static void
+ui_window_dispose (GObject *self)
+{
+  g_action_map_remove_action(G_ACTION_MAP(self), "map-open");
+  g_action_map_remove_action(G_ACTION_MAP(self), "scheme-interrupt");
+
+  /* These should all get disposed when the window disposes, but we'll
+     clear the priv object anyway */
+
+  UI_WINDOW(self)->priv->menubar = NULL;
+  UI_WINDOW(self)->priv->statustab = NULL;
+  UI_WINDOW(self)->priv->cmdwin = NULL;
+  UI_WINDOW(self)->priv->arena = NULL;
+
+  G_OBJECT_CLASS (ui_window_parent_class)->dispose (self);
+}
+
+static void
+ui_window_finalize (GObject *self)
+{
+  GValue *value = NULL;
+
+  g_value_init_from_instance(value, G_OBJECT(NULL));
+  g_object_set_property(self, "map", value);
+  g_object_set_property(self, "robot", value);
+
+  G_OBJECT_CLASS (ui_window_parent_class)->finalize (self);
+}
+
+static void
+ui_window_set_property(GObject *self, guint prop_id, const GValue *value,
+		       GParamSpec *pspec)
+{
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(UI_WINDOW_IS_UI_WINDOW(self));
+
+  UIWindow *win = UI_WINDOW(self);
+  GObject *obj;
 
-#define UI_WINDOW_GET_PRIVATE(obj) \
-    G_TYPE_INSTANCE_GET_PRIVATE(obj, UI_TYPE_WINDOW, UIWindowPrivate)
+  switch (prop_id)
+    {
+    case UI_WINDOW_PROP_MAP:
+      obj = g_value_get_object(value);
+      if(NULL != obj)
+	  g_assert(G_IS_MAP(obj));
+      if(NULL != win->priv->map)
+	g_object_unref(win->priv->map);
+      win->priv->map = g_object_ref(MAP(obj));
+      ui_arena_set_map(UI_ARENA(win->priv->arena), win->priv->map);
+      break;
+    case UI_WINDOW_PROP_ROBOT:
+      obj = g_value_get_object(value);
+      if(NULL != obj)
+	g_assert(G_IS_ROBOT(obj));
+      if(NULL != win->priv->robot)
+	g_object_unref(win->priv->robot);
+      win->priv->robot = g_object_ref(G_ROBOT(obj));
+      ui_status_set_robot(UI_STATUS(win->priv->statustab), win->priv->robot);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(self, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ui_window_get_property(GObject *self, guint prop_id, GValue *value,
+		       GParamSpec *pspec)
+{
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail(UI_WINDOW_IS_UI_WINDOW(self));
 
-G_DEFINE_TYPE(UIWindow, ui_window, GTK_TYPE_WINDOW)
+  UIWindow *win = UI_WINDOW(self);
 
-static void on_ui_window_destroy(GtkWidget *widget, gpointer data);
+  switch (prop_id)
+    {
+    case UI_WINDOW_PROP_MAP:
+      g_value_set_object(value, g_object_ref(G_OBJECT(win->priv->map)));
+      break;
+    case UI_WINDOW_PROP_ROBOT:
+      g_value_set_object(value, g_object_ref(G_OBJECT(win->priv->robot)));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(self, prop_id, pspec);
+      break;
+    }
+}
 
-GtkWidget *ui_window_new()
+static void
+ui_window_class_init (UIWindowClass *klass)
 {
-	return GTK_WIDGET(g_object_new(UI_TYPE_WINDOW, NULL));
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ui_window_dispose;
+  object_class->finalize = ui_window_finalize;
+  object_class->set_property = ui_window_set_property;
+  object_class->get_property = ui_window_get_property;
+
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), UI_WINDOW_PROP_MAP,
+     g_param_spec_object("map",
+			 "GNU Robots Map",
+			 "Map to use for the robot's exploration.",
+			 G_TYPE_OBJECT,
+			 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property
+    (G_OBJECT_CLASS(klass), UI_WINDOW_PROP_ROBOT,
+     g_param_spec_object("robot",
+			 "GNU Robots Robot",
+			 "Robot that will explore the map.",
+			 G_TYPE_OBJECT,
+			 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  /*    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+	widget_class->window_state_event = gedit_window_window_state_event;
+	widget_class->configure_event = gedit_window_configure_event;
+	widget_class->key_press_event = gedit_window_key_press_event; */
 }
 
-static void on_ui_window_destroy(GtkWidget *widget, gpointer data)
+static void
+scheme_interrupt_activated (GSimpleAction  *action,
+			    GVariant       *parameter,
+			    gpointer        user_data)
+{
+  api_interrupt();
+}
+
+static void
+quit_activated (GSimpleAction  *action,
+		GVariant       *parameter,
+		gpointer        user_data)
+{
+  GtkApplication *app;
+
+  app = GTK_APPLICATION (user_data);
+
+  g_application_quit(G_APPLICATION(app));
+}
+
+static void
+on_ui_window_destroy(GtkWidget *widget, gpointer data)
 {
 	gtk_widget_hide(widget);
 	gtk_main_quit();
-	exit_nicely();
 }
 
-static void ui_window_init(UIWindow *window)
+
+static gchar*
+ui_window_choose_file(GtkWidget *parent, gchar *title, gint num_shortcuts, gchar **shortcuts)
 {
-	window->priv = UI_WINDOW_GET_PRIVATE(window);
+  GtkWidget* file_chooser =
+    gtk_file_chooser_dialog_new(title,
+				GTK_WINDOW(parent),
+				GTK_FILE_CHOOSER_ACTION_OPEN,
+				"Cancel", GTK_RESPONSE_CANCEL,
+				"Open", GTK_RESPONSE_ACCEPT,
+				NULL);
+
+  for(gint i = 0; i < num_shortcuts; i++)
+    {
+      if(NULL != shortcuts[i])
+	gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER (file_chooser), shortcuts[i], NULL);
+    }
 
-	g_signal_connect(G_OBJECT(window), "destroy",
-		G_CALLBACK(on_ui_window_destroy), NULL);
+  char *filename = NULL;
+  if (gtk_dialog_run (GTK_DIALOG (file_chooser)) == GTK_RESPONSE_ACCEPT)
+    {
+      filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (file_chooser));
+    }
+  gtk_widget_destroy(file_chooser);
+ 
+  return(filename);
+}
+
+static void
+map_open_activated (GSimpleAction  *action,
+		    GVariant       *parameter,
+		    gpointer        user_data)
+{
+  UIWindow *win = UI_WINDOW(user_data);
+  const gint num_shortcuts = 2;
+  gchar **shortcuts = g_malloc(num_shortcuts * sizeof(gchar**));
+  shortcuts[0] = getenv(MAPS_PATH_ENV);
+  shortcuts[1] = MAPS_PATH;
 
-	gtk_window_set_title(GTK_WINDOW(window), "GNU Robots");
-	gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
+  gchar *map_filename = ui_window_choose_file(GTK_WIDGET(win), "Load Map File", num_shortcuts, shortcuts);
 
-	/* stdio is redirected in here...: */
-	window->priv->cmdwin = ui_cmdwin_new();
-	/* ^so that's needed before guile can start... */
+  g_free(shortcuts);
 
-	window->priv->arena = ui_arena_new();
+  if(NULL != map_filename)
+    {
+      Map *map = win->priv->map;
+      GRobot *robot = win->priv->robot;
+      GError *error = NULL;
+      map_load_filename(map, map_filename, &error);
+      if(NULL != error)
+	{
+	  GtkWidget *dialog =
+	    gtk_message_dialog_new (GTK_WINDOW(win),
+				    GTK_DIALOG_DESTROY_WITH_PARENT,
+				    GTK_MESSAGE_ERROR,
+				    GTK_BUTTONS_CLOSE,
+				    "Error loading file '%s': %s",
+				    map_filename, error->message);
+	  gtk_dialog_run (GTK_DIALOG (dialog));
+	  gtk_widget_destroy (dialog);
+	  //	  g_error("Unable to load map: %s", error->message);
+	  g_error_free(error);
+	}
+      else if(NULL != robot) {
+	map_auto_place_robot(map, robot);
+      }
+    }
 }
 
-void ui_window_postinit(UIWindow *window, Map* volatile* map)
+static void
+about_activated (GSimpleAction  *action,
+		 GVariant       *parameter,
+		 gpointer        user_data)
 {
-	GtkWidget *vbox;
+  const gchar *authors[] = {
+			    "Bradley Smith <brad@brad-smith.co.uk>",
+			    "Daniel M. B. Fortes Manoel <dmbfm2@uol.com.br>",
+			    "James Hall <jhall@freedos.org>",
+			    "Tim Northover <tim@pnorthover.freeserve.co.uk>",
+			    "Zeeshan Ali Khattak <zeenix@gmail.com>",
+			    "Eric P. Hutchins <hutchiep190@potsdam.edu>",
+			    NULL
+  };
 
-	vbox = gtk_vbox_new(FALSE, 2);
+  const gchar *documentors[] = {
+    NULL
+  };
 
-	while(!*map); /* wait for map object creation by guile thread */
+  const gchar *license =
+    "GNU Robots is free software: you can redistribute it and/or modify\n"
+    "it under the terms of the GNU General Public License as published by\n"
+    "the Free Software Foundation, either version 3 of the License, or\n"
+    "(at your option) any later version.\n"
+    "\nGNU Robots is distributed in the hope that it will be useful,\n"
+    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+    "GNU General Public License for more details.\n"
+    "\nYou should have received a copy of the GNU General Public License\n"
+    "along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.\n";
 
-	/* ... because we need guile to load the map before we do this: */
-	ui_arena_set_map(UI_ARENA(window->priv->arena), *map);
+  GdkPixbuf *pixbuf, *transparent;
+  pixbuf = NULL;
+  transparent = NULL;
+  pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar**)robot_xpm);
+  transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
 
-	/* TODO: Add menu first etc */
-	gtk_box_pack_start(GTK_BOX(vbox), window->priv->arena, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), window->priv->cmdwin, TRUE, TRUE, 0);
+  gtk_show_about_dialog (NULL,
+			 "program-name", PACKAGE_NAME,
+			 "version", PACKAGE_VERSION,
+			 "copyright", COPYRIGHT,
+			 "license", license,
+			 "website", PACKAGE_URL,
+			 "comments", "Program a robot to explore on its own.",
+			 "authors", authors,
+			 "documenters", documentors,
+			 "logo", transparent,
+                         "title", "About GTK+ Code Demos",
+			 NULL);
+
+  g_object_unref (transparent);
+}
+
+static GtkWidget*
+ui_window_menu(UIWindow *self)
+{
+  GActionEntry actions[] =
+    {{"map-open", map_open_activated, NULL, NULL, NULL},
+     {"scheme-interrupt", scheme_interrupt_activated, NULL, NULL, NULL}};
+
+  g_action_map_add_action_entries (G_ACTION_MAP (self),
+  				   actions,
+  				   G_N_ELEMENTS (actions),
+  				   self);
+
+  GMenu *gmenu = g_menu_new();
+
+  g_menu_append(gmenu, "_Open Map", "win.map-open");
+  g_menu_append(gmenu, "_Interrupt Scheme", "win.scheme-interrupt");
+
+  GtkWidget *mbar = gtk_header_bar_new();
+  gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(mbar), TRUE);
+  gtk_header_bar_set_title(GTK_HEADER_BAR(mbar), PACKAGE_NAME);
+
+  GtkMenuButton *hamburger = GTK_MENU_BUTTON(gtk_menu_button_new());
+  gtk_menu_button_set_menu_model(hamburger, G_MENU_MODEL(gmenu));
+  gtk_menu_button_set_direction(hamburger, GTK_ARROW_NONE);
+
+  gtk_header_bar_pack_end(GTK_HEADER_BAR(mbar), GTK_WIDGET(hamburger));
+
+  gtk_widget_show_all(mbar);
+  return mbar;
+}
+
+static void
+ui_window_init(UIWindow *self)
+{
+  self->priv = ui_window_get_instance_private (self);
+
+  g_signal_connect(G_OBJECT(self), "destroy",
+		   G_CALLBACK(on_ui_window_destroy), NULL);
+
+  gtk_window_set_title(GTK_WINDOW(self), "GNU Robots");
+
+  self->priv->menubar = ui_window_menu(self);
+  /* stdio is redirected in here...: */
+  self->priv->cmdwin = ui_cmdwin_new();
+  /* ^so that's needed before guile can start... */
+
+  self->priv->arena = ui_arena_new(NULL);
+  self->priv->statustab = ui_status_new();
+
+  gtk_window_set_titlebar(GTK_WINDOW(self), self->priv->menubar);
+  GtkWidget *vbox;
+
+  vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
+
+  gtk_box_pack_start(GTK_BOX(vbox), self->priv->arena, TRUE, TRUE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), self->priv->statustab, TRUE, TRUE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), self->priv->cmdwin, TRUE, TRUE, 0);
+
+  gtk_container_add(GTK_CONTAINER(self), vbox);
+
+  gtk_widget_show_all(vbox);
+  gtk_widget_show_all(GTK_WIDGET(self));
+}
+
+GtkWidget *
+ui_window_new(Map *map, GRobot *robot)
+{
+  g_return_val_if_fail(G_IS_MAP(map), NULL);
+  g_return_val_if_fail(G_IS_ROBOT(robot), NULL);
+
+  return g_object_new(UI_WINDOW_TYPE,
+		      "map", map, "robot", robot,
+		      NULL);
+}
+
+static void
+startup(GtkApplication* app,
+	gpointer        user_data[])
+{
+  GActionEntry actions[] =
+    {{"about", about_activated, NULL, NULL, NULL},
+     {"quit", quit_activated, NULL, NULL, NULL}};
+
+  g_action_map_add_action_entries (G_ACTION_MAP (app),
+  				   actions,
+  				   G_N_ELEMENTS (actions),
+  				   app);
+
+  GMenu *gmenu = g_menu_new();
+
+  g_menu_append(gmenu, "_About", "app.about");
+  g_menu_append(gmenu, "_Quit", "app.quit");
+
+  gtk_application_set_app_menu(app, G_MENU_MODEL(gmenu));
+
+  gtk_application_set_accels_for_action
+    (app, "app.quit", (const gchar*[]) {"<Ctrl>Q", NULL});
+  gtk_application_set_accels_for_action
+    (app, "win.map-open", (const gchar*[]) {"<Ctrl>O", NULL});
+  gtk_application_set_accels_for_action
+    (app, "win.scheme-interrupt", (const gchar*[]) {"<Ctrl>C", NULL});
+}
+
+static void
+activate(GtkApplication* app,
+	 gpointer        user_data[])
+{
+  GtkWidget *window;
+
+  window = ui_window_new(MAP(user_data[0]), G_ROBOT(user_data[1]));
+  gtk_application_add_window(app, GTK_WINDOW(window));
+
+  gtk_widget_show_all (window);
+}
+
+gint
+ui_window_main(Map *map, GRobot *robot, gint argc, gchar **argv)
+{
+  g_return_val_if_fail(G_IS_MAP(map), EXIT_FAILURE);
+  g_return_val_if_fail(G_IS_ROBOT(robot), EXIT_FAILURE);
 
-	gtk_container_add(GTK_CONTAINER(window), vbox);
+  gpointer user_data[2] = {map, robot};
 
-	gtk_widget_show(window->priv->cmdwin);
-	gtk_widget_show(window->priv->arena);
-	gtk_widget_show(vbox);
-	gtk_widget_show(GTK_WIDGET(window));
+  GtkApplication *app = gtk_application_new("org.gnu.gnurobots",
+					    G_APPLICATION_FLAGS_NONE);
+  g_signal_connect(app, "startup", G_CALLBACK(startup), user_data);
+  g_signal_connect(app, "activate", G_CALLBACK(activate), user_data);
+  gint status = g_application_run(G_APPLICATION(app), argc, argv);
+  g_object_unref(app);
 
-	ui_arena_postinit(UI_ARENA(window->priv->arena));
+  return status;
 }
 
-static void ui_window_class_init(UIWindowClass *klass)
+GOptionGroup*
+ui_window_gtk_option_group()
 {
-	g_type_class_add_private(G_OBJECT_CLASS(klass),
-		sizeof(UIWindowPrivate));
+  return gtk_get_option_group(TRUE);
 }
