From e6e7222d5a730368ed4e84c2e0f55427460e5230 Mon Sep 17 00:00:00 2001 From: Bradley Smith Date: Mon, 21 Jan 2008 00:16:45 +0000 Subject: Imported GNU robots from CVS. Signed-off-by: Bradley Smith --- AUTHORS | 9 + COPYING | 351 ++++++++++++++++++++ ChangeLog | 269 +++++++++++++++ Makefile.am | 23 ++ NEWS | 59 ++++ README | 106 ++++++ THANKS | 14 + TODO | 18 + bootstrap.sh | 6 + configure.ac | 145 ++++++++ contrib/Makefile.am | 21 ++ contrib/mapedit.c | 854 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/Makefile.am | 23 ++ doc/Robots-HOWTO | 241 ++++++++++++++ doc/contrib | 20 ++ doc/guile-proj.scm | 44 +++ getopt/Makefile.am | 23 ++ getopt/getopt.c | 683 ++++++++++++++++++++++++++++++++++++++ getopt/getopt.h | 128 ++++++++ getopt/getopt1.c | 172 ++++++++++ include/Makefile.am | 27 ++ include/api.h | 43 +++ include/configs.h | 63 ++++ include/grobot.h | 80 +++++ include/main.h | 10 + include/map.h | 57 ++++ include/sign.h | 3 + include/userinterface.h | 211 ++++++++++++ lib/Makefile.am | 48 +++ lib/cursesplugin.c | 599 +++++++++++++++++++++++++++++++++ lib/cursesplugin.h | 69 ++++ lib/textplugin.c | 444 +++++++++++++++++++++++++ lib/textplugin.h | 63 ++++ lib/x11plugin.c | 840 +++++++++++++++++++++++++++++++++++++++++++++++ lib/x11plugin.h | 102 ++++++ lib/xpm/Makefile.am | 31 ++ lib/xpm/baddie.xpm | 28 ++ lib/xpm/food.xpm | 27 ++ lib/xpm/prize.xpm | 31 ++ lib/xpm/robot.xpm | 28 ++ lib/xpm/robot_east.xpm | 28 ++ lib/xpm/robot_north.xpm | 28 ++ lib/xpm/robot_south.xpm | 28 ++ lib/xpm/robot_west.xpm | 28 ++ lib/xpm/space.xpm | 26 ++ lib/xpm/statusbar.xpm | 58 ++++ lib/xpm/wall.xpm | 26 ++ maps/Makefile.am | 30 ++ maps/maze.map | 18 + maps/pattern.map | 21 ++ maps/small.map | 10 + scheme/Makefile.am | 36 ++ scheme/beep.scm | 30 ++ scheme/greedy.scm | 112 +++++++ scheme/mapper.scm | 827 ++++++++++++++++++++++++++++++++++++++++++++++ scheme/simple.scm | 32 ++ scheme/stop.scm | 16 + scheme/zap.scm | 36 ++ src/Makefile.am | 12 + src/api.c | 160 +++++++++ src/grobot.c | 833 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 565 ++++++++++++++++++++++++++++++++ src/map.c | 297 +++++++++++++++++ src/sign.c | 16 + src/userinterface.c | 192 +++++++++++ 65 files changed, 9478 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 THANKS create mode 100644 TODO create mode 100755 bootstrap.sh create mode 100644 configure.ac create mode 100644 contrib/Makefile.am create mode 100644 contrib/mapedit.c create mode 100644 doc/Makefile.am create mode 100644 doc/Robots-HOWTO create mode 100644 doc/contrib create mode 100644 doc/guile-proj.scm create mode 100644 getopt/Makefile.am create mode 100644 getopt/getopt.c create mode 100644 getopt/getopt.h create mode 100644 getopt/getopt1.c create mode 100644 include/Makefile.am create mode 100644 include/api.h create mode 100644 include/configs.h create mode 100644 include/grobot.h create mode 100644 include/main.h create mode 100644 include/map.h create mode 100644 include/sign.h create mode 100644 include/userinterface.h create mode 100644 lib/Makefile.am create mode 100644 lib/cursesplugin.c create mode 100644 lib/cursesplugin.h create mode 100644 lib/textplugin.c create mode 100644 lib/textplugin.h create mode 100644 lib/x11plugin.c create mode 100644 lib/x11plugin.h create mode 100644 lib/xpm/Makefile.am create mode 100644 lib/xpm/baddie.xpm create mode 100644 lib/xpm/food.xpm create mode 100644 lib/xpm/prize.xpm create mode 100644 lib/xpm/robot.xpm create mode 100644 lib/xpm/robot_east.xpm create mode 100644 lib/xpm/robot_north.xpm create mode 100644 lib/xpm/robot_south.xpm create mode 100644 lib/xpm/robot_west.xpm create mode 100644 lib/xpm/space.xpm create mode 100644 lib/xpm/statusbar.xpm create mode 100644 lib/xpm/wall.xpm create mode 100644 maps/Makefile.am create mode 100644 maps/maze.map create mode 100644 maps/pattern.map create mode 100644 maps/small.map create mode 100644 scheme/Makefile.am create mode 100644 scheme/beep.scm create mode 100644 scheme/greedy.scm create mode 100644 scheme/mapper.scm create mode 100644 scheme/simple.scm create mode 100644 scheme/stop.scm create mode 100644 scheme/zap.scm create mode 100644 src/Makefile.am create mode 100644 src/api.c create mode 100644 src/grobot.c create mode 100644 src/main.c create mode 100644 src/map.c create mode 100644 src/sign.c create mode 100644 src/userinterface.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..1c829e3 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,9 @@ + +Authors of GNU Robots +===================== + +Daniel M. B. Fortes Manoel +James Hall +Tim Northover +Zeeshan Ali Khattak + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f741673 --- /dev/null +++ b/COPYING @@ -0,0 +1,351 @@ + + NOTE! This copyright does *not* cover user programs that run on top +of GNU Robots - this is merely considered normal use of the game, and +does *not* fall under the heading of "derived work". Also note that +the GPL below is copyrighted by the Free Software Foundation, but the +instance of code that it refers to (the GNU Robots game) is +copyrighted by me and others who actually wrote it. + + -Jim Hall + +---------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..d824690 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,269 @@ +2005-09-06 Zeeshan Ali Khattak + * src/main.c src/api.c include/api.h include/main.h: + Converted to the newer scm_* API. + +2004-10-22 Zeeshan Ali Khattak + * lib/cures.c lib/x11.c lib/text.c + lib/curesplugin.c lib/x11plugin.c lib/textplugin.c + lib/curesplugin.h lib/x11plugin.h lib/textplugin.h lib/Makefile.am: + The plugins are also gobjectized now, at last. Had to rename + them and create header files for them. + * include/userinterface.h src/userinterface.c: + The userinterface module is no longer a modules loader but + an (gobject) interface that all display plugins MUST + implement. + * src/main.c: + The module loading/unloading has been moved to the main + module. Also needed to update the interaction with userinterface + module. + * configure.ac: + We needed properties in interfaces which was'nt supported + before glib <= 2.4. + +2004-10-1 Zeeshan Ali Khattak + * configure.ac include/userinterface.h src/userinterface.c: + replaced ltdl with gmodule. You have no idea how much I love + glib :) + +2004-09-29 Zeeshan Ali Khattak + + * include/Makefile.am include/configs.h include/grobot.h + include/userinterface.h include/loadmap.h include/map.h + lib/curses.c lib/text.c lib/x11.c + src/Makefile.am src/grobot.c src/main.c src/userinterface.c + src/map.c: + GObjectized Map code too. Now remains the plugins, but who + will bell the cat? :) + * src/grobot.c: + Act appropriatelly on NULLs rather than g_return_if_fail in + _set_property. + + +2004-09-24 Zeeshan Ali Khattak + + * include/configs.h include/main.h src/grobot.c + src/main.c userinterface.c: + BUGFIX: a refcounting problem because of which the UI module + did'nt get the chance to destroy itself and you know damn well + what this would mean for the curses user. :) + +2004-09-10 Wojciech Polak + + * src/main.c: Changed some printing style (help, version, and etc.). + +2004-08-26 Zeeshan Ali Khattak + + * TODO: Removed things already solved/implemented. + * lib/curses.c lib/text.c lib/x11.c + src/userinterface.c src/main.c src/grobot.c include/userinterface.h: + Expanded the userinterface api to fix a problem I introduced in + the x11 plugin. + +2004-08-22 Wojciech Polak + + * configure.ac: Added AC_PREREQ. + Require at least GNU Autoconf 2.59. + * Makefile.am: Added AUTOMAKE_OPTIONS. + Require at least GNU Automake 1.8.5. + * THANKS: Added new file. + * BUGS: Renamed to TODO. + +2004-08-22 Zeeshan Ali Khattak + + * include/grobot.h include/robots.h src/grobot.c src/robots.c + include/Makefile.am src/Makefile.am + src/api.c src/loadmap.c src/main.c: + Renamed robots module to grobot to make it compatible with + the name of the object: GRobot. + +2004-08-21 Zeeshan Ali Khattak + + Most of the modules have successfully be gobject-ized, except for the + Map loader and the plugins (which is a bit tricky). + +2004-07-27 Wojciech Polak + + Now we can just use `make dist' to create the tarball. + + * contrib/Makefile.am: Added new file. + * doc/Makefile.am: Likewise. + * getopt/Makefile.am: Likewise. + * include/Makefile.am: Likewise. + * lib/xpm/Makefile.am: Likewise. + * maps/Makefile.am: Added EXTRA_DIST. + * scheme/Makefile.am: Added EXTRA_DIST. + * lib/Makefile.am (SUBDIRS): Added xpm. + * Makefile.am (SUBDIRS): Added contrib, doc, getopt, and include. + * configure.ac (AC_CONFIG_FILES): Added new Makefiles. + + * INSTALL: Moved it to the README file + (the specific installation issues). + * README: Updated. + +2004-07-25 Wojciech Polak + + * configure.ac: Renamed from configure.in and slightly improved. + * Makefile.am (EXTRA_DIST): Added `build'. + +2004-07-24 Zeeshan Ali Khattak + + * include/userinterface.h src/userinterface.c: + Rename ui.* to userinterface.* to make it compatible with the + name change of the module (now an object) itself. + +2004-07-23 Zeeshan Ali Khattak + + * include/ui.h src/ui.c: + Some changes to the UI object. + +2004-07-20 Zeeshan Ali Khattak + + * configure.in: + First phase of OOPizing everything. We now require gobject + too. + * include/ui.h src/ui.c src/main.c src/api.c: + The UI module is now implemented as an object. + +2004-07-18 Zeeshan Ali Khattak + + * configure.in Makefile.am lib/Makefile.am: + Plugins are now compiled CONDITIONALLY. + +2004-07-17 Zeeshan Ali Khattak + + * configure.in src/Makefile.am: + We should rely on the guile provided autoconf macros rather + than our own tests + +2004-07-16 Zeeshan Ali Khattak + + * lib/curses.c lib/x11.c: + The robot should'nt just JUMP but move from one block to + another. + * src/main.c src/robots.c: + The list of robots is now implemented as a Doubly-Linked List + and not a static array. + +2004-07-15 Zeeshan Ali Khattak + + * lib/*.c src/main.c src/drawmap.c src/display.c + include/drawmap.h include/display.h + src/Makefile.am: + Removed drawmap module and shifted it's functionality into + the UI module. The previous change did'nt actually solved + the problem it was supposed to, but this one does. + Although this too does'nt solves the turning problem :(. + * lib/x11.c: + Solved the problem of turning not being animated in the x11 + module. + * include/display.h include/ui.h + src/api.c src/main.c src/display.c src/ui.c + src/Makefile.am: + Changed the name of module 'display' to 'ui'. + +2004-07-13 Zeeshan Ali Khattak + + * lib/x11.c: + BUGFIX: zaping and grapping did'nt used to be animated before + the robot-moved. Robot turning has a simillar problem but I'll + have a look at it tomorrow. + +2004-07-12 Zeeshan Ali Khattak + + * maps/Makefile.am scheme/Makefile.am src/Makefile.am Makefile.am + configure.in include/configs.h : + Build-system install is now aware of data files + * bootstrap.sh: + bootstrap.sh now runs the configure script with no options + +2004-07-10 Zeeshan Ali Khattak + + * bootstrap.sh: + Simplied the bootstrap process by using: autoreconf -fisv + * src/drawmap.c: + BUGFIX: replaced hook_add_thing with newer display_add_thing. + display should really be changed to UI. + +2004-07-08 Zeeshan Ali Khattak + + * bootstrap.sh configure.in + include/hooks.h include/display.h include/api.h + src/Makefile.am src/api.c src/display.c src/main.c + lib/Makefile.am lib/text.c lib/curses.c lib/x11.c: + 1 UI is now implemented as dynamically loaded modules + -> plugins. Also added a switch by which user can + specify the module to use. Otherwise grobots shall + choose the best available (x11 if env varriable DISPLAY + is defined, curses otherwise). + + 2 There is ONLY one binary now: grobots + + 3 User dont need to provide full path to the maps or scheme + file. The pkgdatadir and the path declared through the + Env varriables GNU_ROBOTS_MAPS_PATH and + GNU_ROBOTS_SCRIPTS_PATH are searched for the file. + +2004-07-03 Zeeshan Ali Khattak + + * configure.ac lib/Makefile.am src/Makefile.am: + Start to use libtool. + +2004-07-01 Zeeshan Ali Khattak + + * *.h *.c: + Some minor chages and first phase of GLIBization. + +2004-06-30 Zeeshan Ali Khattak + + * curses.c: + Some minor changes. + * bootstrap.sh configure.in Makefile.am src/Makefile.am lib/Makefile.am + Makefile.in src/Makefile.in lib/Makefile.in: + Now we also use automake + +2004-06-29 Zeeshan Ali Khattak + + * hooks.h curses.c text.c x11.c main.c: + Added another function hook_put_string. Using it, added + code to throw the exceptions caught during interractive mode + to the display. + +2004-06-28 Zeeshan Ali Khattak + + * curses.c: + Added Colors. Let there be light! + * api.h curses.c x11.c: + Fixed some timing stuff + * api.h *.map: + A space is now repressented by a space, both in maps and + on the cureses-based display. + * main.c x11.c text.c curses.c: + Added interactive mode. If the scheme file is not specified, + we go into an interactive mode. I'll improve on it tomorrow. + +2004-06-27 Zeeshan Ali Khattak + + * api.h api.c: + Removed a bug i introduced + + * random.h random.c: + Removed. No longer needed after guile 1.3 + + * Makefile.in src/Makefile.in lib/Makefile.in: + Corrected some build problems. + + +2004-06-27 Zeeshan Ali Khattak + + * *.c *.h: + Ran gstreamer provided script gst-indent to indent all the + source files according to what most of the people use rather + than strictly following the GNU Coding Standards. + + * api.h text.c: + defined a global array of strings to translate the directions + to strings + + * scheme/*.scm: + Changed some minor stuff in nice scripts writen a little badly + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..370477e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,23 @@ +## +## Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +AUTOMAKE_OPTIONS = 1.8.5 +SUBDIRS = contrib doc getopt include lib maps scheme src +EXTRA_DIST = build +MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..688f4f0 --- /dev/null +++ b/NEWS @@ -0,0 +1,59 @@ +RELEASE HISTORY FOR GNU ROBOTS: +_____________________________________________________________________ + +1.0D Added some extra documentation (but not much) and the early + beginning of code clean-up. + +1.0C Pawel fixed a bug where the robot was + not able to smell anything to the east or west. + + Fixed a few other bugs. + + Moving the robot around and zapping things and picking things up + just got a little more expensive. If you are running an old + robot under GNU Robots 1.0, you may notice that your robot won't + last as long. I think the new energy "costs" will challenge + people to write more efficient robot programs. + + Removed scm_random and scm_randomize, since the random function + is now provided in GNU Guile 1.3. If you are trying to run GNU + Robots under GNU Guile 1.2 or previous, you will need to patch + it yourself using the src/random.c source file. + + Ran all the .c files through GNU Indent. Sorry about not having + done that before, everyone. + + Added more comments to the sample robot programs. + + Tim Northover contributed a GNU Robots map editor. I have put + this in the `contrib' directory. + +_____________________________________________________________________ + +0.95 - Applied several patches from Steinar Hamre + that fix how `configure' locates + GNU Guile. No feature enhancements. + +0.91 - Fixed a few annoying bugs. No feature enhancements. + +0.9 - Used GNU Autoconf to configure the source for various platforms. + Made robot variables into `long', improved command line using + `getopt', and added Scheme functions to check robot status. + Also, included some sample robots that were submitted. + +0.8 - Added GNU Getopt to scan command line. Added contributions for + X Windows interface, and added cooler XPMs. Changed build + target names, to be more descriptive. Added html + documentation. + +0.77 - Added 'random' and 'randomize' functions. Redefined 'exit' and + 'quit' to exit nicely. Some tweaking to initial energy. + +0.76 - Added code to stop the game when the robot runs out of energy + or shields fall to zero. Reduced globals to only main.c and + api.c. Now builds both the curses and log-file game for 'make + all'. + +0.72 - Fixed a bug during movement. Added a new sample robot. First + release when the game engine actually seems to work. Does not + stop the game when the robot is dead, though. diff --git a/README b/README new file mode 100644 index 0000000..5304fbb --- /dev/null +++ b/README @@ -0,0 +1,106 @@ + +Introduction +============ + +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. + +Installing GNU Robots +====================== + +(0) To build GNU Robots, you *first* need to have already compiled and + installed the GNU Guile library. GNU Robots was built using Guile + 1.3. + + Guile 1.2 will also work, but you will need to provide your own + scm_random() function. See src/random.c + +(1) Type: + + ./configure + + To specify an unusual location for GNU Guile, you can use the + `--with-guile=' option, like this: + + ./configure --with-guile=/home/jhall + + To specify an unusual location for curses, you can use the + `--with-curses=' option, like this: + + ./configure --with-curses=/hub/local + +(2) Look at the generated `Makefile', and check that everything is + okay. Then type: + + make + +Notes +===== + +* There is really one key missing piece in GNU Robots: I had +* originally envisioned a "visual" programming interface for GNU +* Robots, one where you could create a robot program by dropping into +* place icons that represent the robot's actions. There would be one +* icon to tell the robot to move forward, another to have him turn to +* the left or right, and another to have him pick up things or fire +* his little gun. +* +* This programming interface would really just act as a kind of code +* generator; it would write a Scheme program that you could then load +* into GNU Robots. Writing this in GTK+ would seem like a good idea. +* +* If anyone would like to help write this programming interface for +* GNU Robots, please contact me! You may reach me at + + +There are three ways to run GNU Robots: + +1. X Windows ("xrobots") +2. text mode, using curses ("robots") +3. text mode, log file output ("robots_logfile") + +I use the robots_logfile when I am hosting a GNU Robots competition, +because it is not really interesting for me to see how the many robots +are individually interacting with their environments. I am only +interested in the outcome. + +I use the xrobots program when I am running my own GNU Robot game, +because there I *am* interested in what my robot is doing. + +-- + +The "robots" program is a curses-based version of the game, using an +ASCII approximation of the game elements. Your robot will appear as a +"v" when it points South, "^" when it points North, and "<" and ">" +for West and East. Empty spaces are shown as " ", walls as "#", +baddies as "@", and food and prizes as "+" and "$". + +You'll note that this is the same notation used in the GNU Robots map +files. The initial location of the robot is not shown in the map +file, but is always at 1,1 facing East (the upper-left corner is +always 0,0). + +The sample map that is provided as "maps/small.map" is just a single +room, with prizes all along the four walls. The sample robot program +"scheme/simple.scm" knows how to pick up all these prizes and then +quit. Other map files can be found in the maps/ directory, and other +robot programs are in the scheme/ directory. + +The usage for robots, xrobots, robots_logfile is as follows: + + robots [OPTION]... [FILE] + +Options are: + + -f, --map-file=FILE Load map file + -s, --shields=N Set initial shields to N + -e, --energy=N Set initial energy to N + -V, --version Output version information and exit + -h, --help Display this help and exit + +Even though it's not shown, the `-f' option to load a map file is +required by GNU Robots. Otherwise, the game will not be able to open +a map for your robot to explore! + diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..3942c1f --- /dev/null +++ b/THANKS @@ -0,0 +1,14 @@ +GNU Robots THANKS file + +GNU Robots was originally written by Jim Hall. +Other people contributed by reporting problems, +suggesting various improvements, or submitting +actual code. Here is a list of those people. + +Daniel M. B. Fortes Manoel +David Madore +Pawel Turnau +Steinar Hamre +Tim Northover +Zeeshan Ali Khattak + diff --git a/TODO b/TODO new file mode 100644 index 0000000..704142f --- /dev/null +++ b/TODO @@ -0,0 +1,18 @@ + +KNOWN BUGS: +_____________________________________________________________________ + +_____________________________________________________________________ + +THINGS TO DO: +_____________________________________________________________________ + +* Improve the graphics, to make everything look better. I think I + need a graphic designer to help me do this. GTK+ would be nice. + +* In version 2.0, more creatures will get added. The things called + "baddie" and "food" and "prize" will be generic descriptors. For + example, "food" will be a generic name for "cookie" or "donut", so + that (robot-feel 'cookie) will fail if the thing is a donut, but + (robot-feel 'food) will still work. + diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..607ca68 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,6 @@ +#! /bin/sh + +autoreconf -fisv + +echo "Running configure with no arguments" +./configure diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..2c16a6a --- /dev/null +++ b/configure.ac @@ -0,0 +1,145 @@ +dnl +dnl configure.ac for GNU Robots +dnl Copyright (C) 1998 Jim Hall, jhall1@isd.net +dnl +dnl GNU Robots is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl GNU Robots is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GNU Robots; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl + +dnl Process this file with autoconf to produce a configure script. +AC_INIT([GNU Robots], [1.1], [zeenix@gmail.com], [robots]) +AC_PREREQ([2.59]) +AM_CONFIG_HEADER([config.h]) +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_SRCDIR([include/api.h]) +AM_INIT_AUTOMAKE + +dnl Checks for programs. +AC_PROG_CC + +dnl libtool stuff +AC_LIBTOOL_DLOPEN +dnl AC_LIBLTDL_CONVENIENCE +AC_DISABLE_STATIC +AC_PROG_LIBTOOL + +AC_SUBST(INCLTDL) +AC_SUBST(LIBLTDL) + +dnl Locate X Windows +dnl We need AC_PATH_XTRA to also locate extra libaries X depends on. +dnl It will ``export'' X_CFLAGS, X_PRE_LIBS, X_EXTRA_LIBS and X_LIBS, +dnl but for some reason we need to add -lX11 ourselves. +AC_PATH_XTRA + +if test "$have_x" = yes; then + x_libs="-lX11" + use_x11="yes" + +dnl Checks for more X11 libraries, these go to X_LIBS. +AC_CHECK_LIB(Xpm, XpmCreatePixmapFromData, + x_libs="-lXpm $x_libs" + use_x11="yes", + AC_MSG_WARN("can't find libXpm, X11 plugin shall not be compiled") + use_x11="no", + $X_LIBS $x_libs $X_PRE_LIBS $X_EXTRA_LIBS) + +dnl finished with X, so we update X_LIBS. +X_LIBS="$X_LIBS $x_libs" + +else + AC_MSG_WARN("can't find X windows, X11 plugin shall not be compiled") + use_x11="no", +fi + +AM_CONDITIONAL(USE_X11, test "$use_x11" = "yes") + +dnl Check for curses, add to CURSES_LIBS, and CURSES_CFLAGS. +dnl At this time, only ncurses is supported. +dnl We should check for SYSV curses too. +AC_CHECK_HEADERS(ncurses.h, + use_curses="yes", + AC_MSG_WARN("Can't find ncurses. Curses plugin shall not be compiled.") + use_curses="no") + +dnl we dont need other checks if the headers are not found +if test "$use_curses" = "yes" +then + +dnl allow user to specify directory where to find ncurses +if test -n "$with_ncurses" +then + ncurses_cflags="-I$with_ncurses/include" + ncurses_libs="-L$with_ncurses/lib" +fi + +AC_CHECK_LIB(ncurses, initscr, + CURSES_LIBS="$ncurses_libs -lncurses" + CURSES_CFLAGS="$ncurses_cflags" + use_curses="yes", + AC_MSG_WARN("Can't find ncurses. Curses plugin shall not be compiled.") + use_curses="no") + +AC_SUBST(CURSES_LIBS) +AC_SUBST(CURSES_CFLAGS) + +fi + +AM_CONDITIONAL(USE_CURSES, test "$use_curses" = "yes") + +dnl Check for math library +AC_CHECK_LIB(m, pow) + +dnl Check for guile +GUILE_FLAGS + +dnl Some sytems need -ldl for dynamic library support. +dnl AC_CHECK_LIB(dl, dlopen) + +dnl Check for glib2 +PKG_CHECK_MODULES(GLIB2,glib-2.0 >= 2.4 gobject-2.0 gmodule-2.0,HAVE_GLIB2=yes,HAVE_GLIB2=no) +AC_SUBST(GLIB2_LIBS) +AC_SUBST(GLIB2_CFLAGS) + +schemedir="\$(pkgdatadir)/scheme" +AC_SUBST(schemedir) + +mapsdir="\$(pkgdatadir)/maps" +AC_SUBST(mapsdir) + +if test "x$HAVE_GLIB2" = "xno"; then + AC_MSG_ERROR([GNU Robots requires GLib2 to compile.]) +fi + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST + +dnl Done. +AC_CONFIG_FILES([Makefile + contrib/Makefile + doc/Makefile + getopt/Makefile + include/Makefile + lib/Makefile + lib/xpm/Makefile + maps/Makefile + scheme/Makefile + src/Makefile + ]) +AC_OUTPUT + diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 0000000..e2e5a02 --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,21 @@ +## +## contrib/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +EXTRA_DIST =\ + mapedit.c + diff --git a/contrib/mapedit.c b/contrib/mapedit.c new file mode 100644 index 0000000..fc16bda --- /dev/null +++ b/contrib/mapedit.c @@ -0,0 +1,854 @@ +/* Map editor for GNU robots game */ + +/* Copyright (C) 2000 Tim Northover, tim@pnorthover.freeserve.co.uk + using xpms from Tim Whittock*/ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* compile with: + gcc mapedit.c -omapedit -I../lib `gtk-config --cflags --libs` */ + +/* Usage: mapedit [filename] */ + +#include /* xwindows functions */ +#include /* for file functions */ +#include /* for atoi */ + +/* indexes into the blocks array */ + +#define SPACE 0 +#define FOOD 1 +#define WALL 2 +#define BADDIE 3 +#define PRIZE 4 + +/* this is 1 more than the real size to allow for a border around + images: */ + +#define IMGSIZE 17 + +/* Stores information on blocks for display and storage */ + +typedef struct +{ + int type; /* probably unnecessary at the moment, + but potentially useful (pointers) */ + char *name; /* for display */ + char save_char; /* character in map files */ + char **xpm_data; /* again at the moment not really necessary, + but potentially */ + GdkPixmap *pixmap; /* The image in gtk useable form */ +} +BlockType; + +typedef struct +{ + int width, height; + char *data; /* will contain index into blocks array */ +} +GridType; + +GridType grid; /* The internal grid representation */ + +/* Now define known blocks -- leave xpm_data & pixmap for later init */ + +BlockType blocks[] = { {SPACE, "Empty Space", '.', NULL, NULL}, +{FOOD, "Food", '+', NULL, NULL}, +{WALL, "Wall", '#', NULL, NULL}, +{BADDIE, "Baddie", '@', NULL, NULL}, +{PRIZE, "Prize", '$', NULL, NULL} +}; + +int nblocks = 5; /* number of blocks */ + +GtkEntry *width_text, *height_text; /* for use by the size setting + button -- not ideal as + globals but simpler */ + +int dragging; /* whether to change "grid" -- + modified by button_press & + button_release */ + +int current_tool; /* index into blocks */ + +GtkWidget *grid_window; /* main window for displaying map -- + used by various functions */ + +/* initialisation/end funcs */ + +/* blocks global */ +void init_blocks (); + +/* initialises global "grid" -- doesn't touch anything X related */ +void init_grid (char *filename); + +/* used at beginning and in load button func to actually set "grid" */ +void load_file (char *filename); + +/* used to report failure to load/save file */ +void message_box (char *title, char *message); + +/* ATM only frees "grid.data" */ +void freestuff (); + +/* creates a box with pixmap & label -- for toolbox display */ +GtkWidget *create_pixmap_with_label (GtkWidget * parent, GdkPixmap * pixmap, + char *text); +/* handler function when new tool is clicked */ +void change_tool (GtkWidget * widget, gpointer data); + + +/* Grid update hanlders */ + +/* (mouse) button pressed in map window -- start draw */ +void button_press (GtkFixed * fixed, GdkEventButton * event, gpointer data); + +/* mouse moved in map window -- checks "dragging" */ +void mouse_move (GtkFixed * fixed, GdkEventMotion * event, gpointer data); + +/* stop drawing */ +void button_release (GtkFixed * fixed, GdkEventButton * event, gpointer data); + +/* sets "grid" and individual pixmap on map window */ +void update_grid (GtkFixed * fixed, int x, int y); + +/* creates radio button vertical box (for tools) */ +GtkWidget *create_toolbox_box (GtkWidget * parent, GtkSignalFunc func); + +/* creates box with load, save... */ +GtkWidget *create_command_box (char *filename); + +/* creates window for both of above widgets */ +GtkWidget *create_toolbox_window (char *filename); + +/* creates map display window */ +GtkWidget *create_grid_window (); + +/* clicked routine for Save */ +void save_button (GtkWidget * widget, GtkEntry * textbox); + +/* clicked routine for Load */ +void load_button (GtkWidget * widget, GtkEntry * textbox); + +/* clicked routine for Set Size */ +void set_size (GtkWidget * widget, gpointer data); + +/* destroys current map window and creates a new one from "grid" */ +void recreate_grid_window (); + +int +main (int argc, char **argv) +{ + char *filename; + FILE *fp; /* for checking filename given */ + + GtkWidget *toolbox; /* main window -- currently unused but + saved for future -- and it's nice + to know I know something about the + window I've created */ + + /* Global initialisation */ + + if (argc > 1) /* check if filename specified */ + { + filename = argv[1]; + fp = fopen (filename, "r"); + if (!fp) + { + g_print ("Could not open file '%s'\n", filename); + g_print ("Usage: %s [filename]\n", argv[0]); + return 1; + } + fclose (fp); + } + else + filename = NULL; + + gtk_init (&argc, &argv); /* must be before any other gtk_functions... */ + current_tool = SPACE; + init_blocks (); /* ...like the ones in here */ + + init_grid (filename); /* loads file or creates 16x16 grid + with SPACE */ + + toolbox = create_toolbox_window (filename); /* main window creation */ + + gtk_main (); /* Main message loop -- Go! */ + + return 0; /* Everything went fine (from our + perspective) */ +} + +/* initialises the rest of the "blocks" array */ +void +init_blocks () +{ + int i; /* counter */ + GdkColormap *colour; /* necessary for creating pixmaps + -- otherwise warnings ensue */ +#include "xpm/food.xpm" +#include "xpm/space.xpm" +#include "xpm/prize.xpm" +#include "xpm/baddie.xpm" +#include "xpm/wall.xpm" + + /* These could be set globally, but this avoids making the xpm data + global */ + + blocks[0].xpm_data = space_xpm; + blocks[1].xpm_data = food_xpm; + blocks[2].xpm_data = wall_xpm; + blocks[3].xpm_data = baddie_xpm; + blocks[4].xpm_data = prize_xpm; + + colour = gdk_colormap_get_system (); /* needed to prevent warnings */ + + for (i = 0; i < nblocks; i++) + { + blocks[i].pixmap = + gdk_pixmap_colormap_create_from_xpm_d (NULL, colour, NULL, NULL, + blocks[i].xpm_data); + } +} + +/* either loads file or creates blank map -- called at beginning */ + +void +init_grid (char *filename) +{ + int i; /* counter */ + + if (filename) /* file specified -- only false first + time if no command line options */ + { + load_file (filename); + } + + else + { + grid.width = grid.height = 16; + + grid.data = (char *) g_malloc (grid.width * grid.height * sizeof (char)); /* grab memory */ + + for (i = 0; i < grid.width * grid.height; i++) /* initialise grid */ + { + grid.data[i] = SPACE; + } + } +} + +/* callback for radio buttons -- changes tool */ + +void +change_tool (GtkWidget * widget, gpointer data) +{ + /* can't just pass integer -- need GINT_TO_POINTER and converse */ + + current_tool = GPOINTER_TO_INT (data); +} + +/* This creates a pixmap and a label from data -- returns hbox with contents */ + +GtkWidget * +create_pixmap_with_label (GtkWidget * parent, GdkPixmap * pixmap, char *text) +{ + GtkWidget *pixmapwid, *box, *label; /* All variables -- easier + than doubling up */ + + box = gtk_hbox_new (FALSE, 2); + + /* Create them */ + + pixmapwid = gtk_pixmap_new (pixmap, NULL); + gtk_widget_show (pixmapwid); + label = gtk_label_new (text); + gtk_widget_show (label); + + /* Put them in the box */ + + gtk_box_pack_start (GTK_BOX (box), pixmapwid, FALSE, FALSE, 4); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 4); + gtk_widget_show (box); + + return box; +} + +/* This creates a vbox with each tool in linked to specified function */ + +GtkWidget * +create_toolbox_box (GtkWidget * parent, GtkSignalFunc func) +{ + int i; /* counter */ + GtkWidget *box; /* Box to return */ + GtkWidget *button; /* radio button for tools */ + GtkWidget *pixmap; /* Picture to display -- with label */ + + box = gtk_vbox_new (FALSE, 2); + + for (i = 0, button = NULL; i < nblocks; i++) + { + pixmap = create_pixmap_with_label (parent, blocks[i].pixmap, blocks[i].name); /* actually a box -- but contains a pixmap */ + + /* create button radio group -- use ?: to prevent special case */ + + button = + gtk_radio_button_new (button ? + gtk_radio_button_group (GTK_RADIO_BUTTON + (button)) : NULL); + + gtk_container_add (GTK_CONTAINER (button), pixmap); + + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (i)); + + gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 3); + + gtk_widget_show (button); + } + + return box; /* return a nice package */ +} + +/* callback for clicking save button -- currently only way so it does + th work here */ + +void +save_button (GtkWidget * widget, GtkEntry * textbox) +{ + /* textbox is passed as data in ...signal_connect */ + + int i, j; /* counters -- width & height */ + FILE *fp; + char *filename; /* obtained from "textbox" */ + + if (!grid_window) /* Don't save if they've closed the window */ + { + return; + } + + /* Open and check file */ + + filename = gtk_entry_get_text (GTK_ENTRY (textbox)); + fp = fopen (filename, "w"); + + if (!fp) /* can't proceed */ + { + message_box ("Error:", "Could not save file"); + return; /* Can't proceed */ + } + + /* Write file */ + + for (i = 0; i < grid.height; i++) + { + for (j = 0; j < grid.width; j++) + fputc (blocks[(int) grid.data[i * grid.width + j]].save_char, fp); /* put the save_char for correct "blocks" entry */ + + fputc ('\n', fp); /* newline */ + } + + fclose (fp); +} + +/* actual function to load file -- don't put more spare \n's at end + than width -- it won't like it */ + +void +load_file (char *filename) +{ + FILE *fp; + int length; /* length of file */ + + int ctr, i, first; /* counter into data, + counter for finding correct block, + whether first line */ + + char *data, c; /* buffer and current character */ + + /* standard open and check... */ + + fp = fopen (filename, "r"); + + if (!fp) + { + message_box ("Error", "Could not open file"); + return; + } + + /* find file size -- and allocation */ + + fseek (fp, 0, SEEK_END); + length = ftell (fp); + rewind (fp); /* for reading */ + + data = (char *) g_malloc (length * sizeof (char)); + + /* will be larger than necessary -- but only by (height) bytes + usually */ + + ctr = 0; /* index into "data" */ + + first = 1; /* first line */ + + while ((c = fgetc (fp)) != EOF) + { + /* This is executed more frequently than strictly necessary + (ideally only on \n as well as normal) -- but this ensures + things work */ + + data[ctr] = WALL; /* Sensible default (used in xrobots + as default) */ + + /* find correct block */ + + for (i = 0; i < nblocks; i++) + if (c == blocks[i].save_char) + { + data[ctr++] = i; + break; + } + + if (c == '\n' && first) /* end of first line -- now know width */ + { + first = 0; /* not first any more */ + grid.width = ctr; + } + } + fclose (fp); /* Done here */ + + grid.height = length / (grid.width + 1); /* height * (width + newlines) == file size (roughly) */ + + g_free (grid.data); /* free the old... */ + + grid.data = data; /* ...and replace with new */ +} + +/* Actual load button clicked -- calls load_file for hard work*/ + +void +load_button (GtkWidget * widget, GtkEntry * textbox) +{ + char *filename; + + filename = gtk_entry_get_text (textbox); + load_file (filename); + + recreate_grid_window (); /* destroys current window and creates new */ +} + +/* Callback for Setting Size button */ + +void +set_size (GtkWidget * widget, gpointer data) +{ + int x, y, i; + + x = atoi (gtk_entry_get_text (width_text)); + y = atoi (gtk_entry_get_text (height_text)); + if (x > 0 && y > 0 && (x != grid.width || y != grid.height)) + /* check data in boxes (don't change to same size as now) */ + { + grid.width = x; + grid.height = y; + + if (grid.data) + g_free (grid.data); + + grid.data = + (char *) g_malloc (grid.width * grid.height * sizeof (char)); + + for (i = 0; i < grid.width * grid.height; i++) /* note that current grid is not saved or scaled */ + grid.data[i] = SPACE; + + recreate_grid_window (); /* inform gtk of change */ + } +} + +/* destroys current map window and creates new */ + +void +recreate_grid_window () +{ + if (grid_window) /* just in case -- person could have + closed the window */ + { + gtk_widget_destroy (grid_window); + } + grid_window = create_grid_window (); +} + +/* create box of functions -- very tedious */ + +GtkWidget * +create_command_box (char *filename) +{ + char str[255]; /* to set text boxes based on + width/height -- I know it's + unlikely that it will be 255 chars + long */ + + GtkWidget *vbox, *sizebox; /* vbox == main box for widgets, + sizebox == box for Entrys & labels + for size */ + + GtkWidget *button, *textbox; /* generic button followed by textbox + for filename */ + + GtkWidget *label, *xsize, *ysize; /* stuff for setting size */ + + vbox = gtk_vbox_new (FALSE, 3); /* create main box */ + + /* create filename textentry */ + + textbox = gtk_entry_new (); + + gtk_entry_set_text (GTK_ENTRY (textbox), + filename ? filename : "newmap.map"); + + gtk_widget_show (textbox); + + gtk_box_pack_start (GTK_BOX (vbox), textbox, FALSE, FALSE, 2); + + /* create save button & link with callback */ + + button = gtk_button_new_with_label ("Save"); + + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (save_button), textbox); /* note it gets the filename as data */ + + gtk_widget_show (button); + + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2); + + /* create the load button & link */ + + button = gtk_button_new_with_label ("Load"); + + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (load_button), textbox); /* Also gets callback */ + + gtk_widget_show (button); + + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2); + + /* create row for size controls -- except actual button */ + + sizebox = gtk_hbox_new (FALSE, 1); + + /* X label & text */ + + label = gtk_label_new ("X:"); + gtk_widget_show (label); + + gtk_box_pack_start (GTK_BOX (sizebox), label, FALSE, FALSE, 1); + + xsize = gtk_entry_new_with_max_length (3); + width_text = GTK_ENTRY (xsize); + sprintf (str, "%d", grid.width); + gtk_entry_set_text (GTK_ENTRY (xsize), str); + gtk_widget_set_usize (xsize, 30, 20); + gtk_widget_show (xsize); + + gtk_box_pack_start (GTK_BOX (sizebox), xsize, FALSE, FALSE, 1); + + /* Y label && text */ + + label = gtk_label_new ("Y:"); + gtk_widget_show (label); + + gtk_box_pack_start (GTK_BOX (sizebox), label, FALSE, FALSE, 1); + + ysize = gtk_entry_new_with_max_length (3); + height_text = GTK_ENTRY (ysize); + sprintf (str, "%d", grid.height); + gtk_entry_set_text (GTK_ENTRY (ysize), str); + gtk_widget_set_usize (ysize, 30, 20); + gtk_widget_show (ysize); + + gtk_box_pack_start (GTK_BOX (sizebox), ysize, FALSE, FALSE, 1); + + gtk_widget_show (sizebox); + gtk_box_pack_start (GTK_BOX (vbox), sizebox, FALSE, FALSE, 2); + + /* Done size controls */ + + /* Actual size button */ + + button = gtk_button_new_with_label ("Set Size"); + + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (set_size), NULL); + + gtk_widget_show (button); + + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2); + + return vbox; /* return nice simple package */ +} + +/* This creates and initialises the toolbox window (actually the MAIN + WINDOW)*/ + +GtkWidget * +create_toolbox_window (char *filename) +{ + GtkWidget *window; /* main window */ + + GtkWidget *toolbox, *command_box, *hbox; /* three important components (hbox to store others) */ + + grid_window = create_grid_window (); /* map window controlled by + this -- needs to be able to + load */ + + /* Create our window -- its closure causes the app to quit... */ + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "Toolbox"); + + gtk_signal_connect (GTK_OBJECT (window), "delete_event", + GTK_SIGNAL_FUNC (gtk_exit), NULL); /* ... as setup on this line */ + + /* create and display two panes of display */ + + toolbox = create_toolbox_box (window, change_tool); + gtk_widget_show (toolbox); + command_box = create_command_box (filename); + gtk_widget_show (command_box); + + /* join two panes together */ + + hbox = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (hbox), toolbox, FALSE, FALSE, 5); + gtk_box_pack_start (GTK_BOX (hbox), command_box, FALSE, FALSE, 5); + gtk_widget_show (hbox); + gtk_container_add (GTK_CONTAINER (window), hbox); + + /* show window */ + + gtk_widget_show (window); + return window; /* give it back to main */ +} + +/* receives position to change -- puts correct pixmap in place */ + +/* See Fixed(Wibble1) in create_grid_window for description of fixed */ + +void +update_grid (GtkFixed * fixed, int x, int y) +{ + int length, i, dx, dy; /* length == how many children are in fixed, + i == counter for iteration, + dx, dy == How far apart the current position is from the desired one */ + + GList *children; /* children of fixed -- i.e. the + pixmap widgets */ + + GtkFixedChild *child; /* individual child of fixed */ + + grid.data[x + y * grid.width] = current_tool; + + /* initialise for search */ + + children = fixed->children; + length = g_list_length (children); + + /* start finding -- this is a horrible kludge, but the simplest to + code */ + + for (i = 0; i < length; i++) + { + child = (GtkFixedChild *) g_list_nth_data (children, i); /* get current thing */ + + dx = x - (child->x / IMGSIZE); /* position in pixels -- must be changed to reality */ + + dy = y - (child->y / IMGSIZE); + + if (dx == 0 && dy == 0) /* if correct position */ + { + gtk_pixmap_set (GTK_PIXMAP (child->widget), + blocks[current_tool].pixmap, NULL); /* change the pixmap */ + + break; /* and quit -- don't compound folly */ + } + } +} + +/* handler for drawing -- set dragging true */ + +void +button_press (GtkFixed * fixed, GdkEventButton * event, gpointer data) +{ + int i, j; /* positions */ + + dragging = 1; + + i = (int) (event->x / IMGSIZE); + j = (int) (event->y / IMGSIZE); + + update_grid (fixed, i, j); /* set current square so user doesn't + have to move mouse */ +} + +/* callback for mouse moved on map window -- checks dragging */ + +/* This is not ideal as it puts extra load on the cpu when the mouse + is moving in the map window */ + +void +mouse_move (GtkFixed * fixed, GdkEventMotion * event, gpointer data) +{ + int i, j; + + i = (int) (event->x / IMGSIZE); + j = (int) (event->y / IMGSIZE); + if (dragging) + update_grid (fixed, i, j); +} + +/* stop drawing */ + +void +button_release (GtkFixed * fixed, GdkEventButton * event, gpointer data) +{ + dragging = 0; +} + +/* called when user closes window -- sets grid_window to NULL to + prevent recreate_grid_window segfaulting */ + +void +destroy_func (GtkWidget * widget, gpointer data) +{ + *((GtkWidget **) data) = NULL; + grid_window = NULL; +} + +/* Does the donkeywork in creating the map view */ + +GtkWidget * +create_grid_window () +{ + int i, j, type; /* i, j for iteration through "grid.data", + type == index into blocks -- stored + for convenience */ + + GtkWidget *window; /* actual window */ + GtkWidget *fixed; /* layout manager for pictures */ + + GtkWidget *pixmap; /* temporary sotrage for each picture + while in use */ + + /* first create and set up main features */ + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (destroy_func), &window); + gtk_window_set_title (GTK_WINDOW (window), "Map View"); + + /* Fixed(Wibble1): + fixed is a method of storing widgets by position, + 1. I create a sequence of pixmap widgets and place them in correct positions, + 2. I initialise them to the correct picture, + 3. I link the fixed's press, motion & release events to something to find which + pixmap is under pointer and change picture & "grid" */ + + fixed = gtk_fixed_new (); + + for (i = 0; i < grid.width; i++) + for (j = 0; j < grid.height; j++) + { + type = grid.data[i + j * grid.width]; + pixmap = gtk_pixmap_new (blocks[type].pixmap, NULL); + + gtk_fixed_put (GTK_FIXED (fixed), pixmap, i * IMGSIZE, j * IMGSIZE); /* position */ + + gtk_widget_show (pixmap); + } + + /* enable events needed... */ + + gtk_widget_set_events (fixed, + GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK); + + /* ... and take control */ + + gtk_signal_connect (GTK_OBJECT (fixed), "button_press_event", + GTK_SIGNAL_FUNC (button_press), NULL); + + gtk_signal_connect (GTK_OBJECT (fixed), "motion_notify_event", + GTK_SIGNAL_FUNC (mouse_move), NULL); + + gtk_signal_connect (GTK_OBJECT (fixed), "button_release_event", + GTK_SIGNAL_FUNC (button_release), NULL); + + gtk_widget_show (fixed); + + gtk_container_add (GTK_CONTAINER (window), fixed); /* window only contains pictures */ + + gtk_widget_show (window); + + return window; /* give the window back to + create_toolbox_window */ +} + +/* Free all the rubbish */ + +void +freestuff () +{ + g_free (grid.data); +} + +/* Doesn't look very nice but is functional (message box and code) */ + +void +message_box (char *title, char *text) +{ + GtkWidget *label, *window, *button, *box; + + window = gtk_window_new (GTK_WINDOW_DIALOG); /* make window */ + + gtk_window_set_title (GTK_WINDOW (window), title); + + label = gtk_label_new (text); /* Label as required */ + + gtk_widget_show (label); + + button = gtk_button_new_with_label ("OK"); /* Just an OK button + that kills the + window */ + + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + (gpointer) window); + + gtk_widget_show (button); + + box = gtk_vbox_new (FALSE, 3); /* Put everything in... */ + + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3); + gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 3); + + gtk_widget_show (box); + + gtk_container_add (GTK_CONTAINER (window), box); + + gtk_widget_show (window); /* ... and show */ + + /* Don't return anything -- no interaction necessary */ +} diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..8983896 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,23 @@ +## +## doc/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +EXTRA_DIST =\ + contrib\ + guile-proj.scm\ + Robots-HOWTO + diff --git a/doc/Robots-HOWTO b/doc/Robots-HOWTO new file mode 100644 index 0000000..d15a18c --- /dev/null +++ b/doc/Robots-HOWTO @@ -0,0 +1,241 @@ +Robots-HOWTO + +Writing programs for GNU Robots + +Jim Hall + +_____________________________________________________________________ + +Introduction +_____________________________________________________________________ + +Hi, and welcome to GNU Robots! I was going to call this file +`api.txt' but I figured that wouldn't be very helpful. And I can tell +right off the bat that I'm not just going to talk about the GNU Robots +programming API. But I will talk about that quite a bit, since it's so +important to using 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? + +_____________________________________________________________________ + +Writing programs +_____________________________________________________________________ + +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 `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 +"function" approach to programming a GNU Robot. Many people will +refer to this as a "divide and conquer" strategy, and I find it works +well. + +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: + + (define (grab-and-move) + (robot-grab) + (robot-move 1) + ) + +The `define' statement is from Scheme, and allows us to define a +function called `grab-and-move'. The function consists of two +statements: `robot-grab' to pick up a thing immediately in front of +the robot, and `robot-move' to move the robot a given number of +spaces. + +That was easy. Now, let's try another. + +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 `grab-and-move' function that we defined above. + + (define (grab-prize) + (if (eqv? (robot-feel "prize") #t) (grab-and-move)) + ) + +This is not so scary a statement, if we look at it a piece at a time. +The `eqv?' statement compares the truth value of two expressions. In +this case, we compare a test expression against a literal true value +(`#t'). Our test expression is `(robot-feel "prize")' which will +return a `#t' value if the little robot is able to detect a prize in +the space immediately in front of it. + +If the `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 `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: + + (define (grab-food) + (if (eqv? (robot-feel "food") #t) (grab-and-move)) + ) + +As you can see, the `grab-food' function is virtually identical to the +`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. + +_____________________________________________________________________ + +Robot actions +_____________________________________________________________________ + +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 `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 `N' after the function name. +If the argument is a GNU Robots "thing" then I'll print `TH' after the +function name. If the primitive does not take an argument, I will +print nothing. + + PRIMITIVE ENERGY ACTION +===================================================================== + robot-turn N -2 Turns the robot N spaces to the right + or left. If `N' is a positive number, + the robot turns to the right. If `N' + is negative, the robot turns to the + left. Every turn has a cost of -2, so + if you `robot-turn 2' then the cost is + -4 to your energy. +--------------------------------------------------------------------- + robot-move N -2 Moves the robot N spaces forwards or + backwards. If `N' is a positive + number, the robot moves forwards. If + `N' is negative, the robot moves + backwards. Every move has a cost of + -2, so if you `robot-move 2' then the + cost is -4 to your energy. +--------------------------------------------------------------------- + robot-smell TH -1 Smells for a thing. If the thing is + in the surrounding eight spaces, the + function returns a true (`#t') value. +--------------------------------------------------------------------- + robot-feel TH -1 Feels for a thing in the space directly + in front of the robot. If the thing is + detected in that space, the function + returns a true (`#t') value. Note that + you can feel a baddie, and not take any + damage. +--------------------------------------------------------------------- + robot-look TH -1 Looks ahead across empty spaces for a + particular thing. If the thing is + seen, the function return returns a + true (`#t') value. Note that this is + sort of a braindead 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. +--------------------------------------------------------------------- + robot-grab -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 (`#t') if the + robot was able to pick the thing up. +--------------------------------------------------------------------- + robot-zap -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 + (`#t') value. +--------------------------------------------------------------------- + robot-stop 0 Exits GNU Robots immediately. +--------------------------------------------------------------------- + robot-get-shields 0 Returns the level of the little robot's + shields. +--------------------------------------------------------------------- + robot-get-energy 0 Returns the level of the little robot's + energy. +--------------------------------------------------------------------- + robot-get-score 0 Returns the robot's score (how many + prizes have been picked up.) +===================================================================== + + +And what kinds of "things" are out there to be detected by +`robot-smell', `robot-feel' and `robot-look'? Here is a list of all +possible things in the GNU Robots world: + + THING DESCRIPTION +===================================================================== + 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. +--------------------------------------------------------------------- + space An empty space. There's nothing interesting here. +--------------------------------------------------------------------- + food Yum! A health item that will help to restore +10 points + of your robot's energy levels. +--------------------------------------------------------------------- + prize Pick these up! This will add +1 point to your score. + Remember, the goal of GNU Robots is to get the highest + score! +--------------------------------------------------------------------- + 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. +===================================================================== + + +_____________________________________________________________________ + +Go try it! +_____________________________________________________________________ + +Okay, that's about all the information you should need to set you on +the way to writing really cool GNU Robots programs. Have fun! diff --git a/doc/contrib b/doc/contrib new file mode 100644 index 0000000..c4c6d74 --- /dev/null +++ b/doc/contrib @@ -0,0 +1,20 @@ +CONTRIBUTORS TO GNU ROBOTS: +_____________________________________________________________________ + +* Jim Hall, + author + +* Tom Whittock, + X11 interface + +* Tim Northover, tim@pnorthover.freeserve.co.uk + mapedit.c (GNU Robots map editor, for GNOME) + +* Kyle Hasselbacher + 'greedy.scm' and 'mapper.scm' + +* david.madore@ens.fr + Helped fix `configure.in' to detect X Windows + +* Steinar Hamre + Patched `configure.in' and several other files to better detect GNU Guile diff --git a/doc/guile-proj.scm b/doc/guile-proj.scm new file mode 100644 index 0000000..b07020c --- /dev/null +++ b/doc/guile-proj.scm @@ -0,0 +1,44 @@ +;;;How To Submit A Project +;;; +;;;The first thing to do is look at the Guile Project Mail Submission +;;;doc. This will tell you all you need to know about describing your +;;;project. Then, send a mail to me (Greg.Harvey@thezone.net) with the +;;;subject "Guile Project Submission". Optionally, you can send it to +;;;(Greg.Harvey+guile-project@thezone.net). Please do one or the +;;;other, or else there's a good chance it'll end up in the spam +;;;box. The body should contain your submission(s). +;;; +;;;How To Update A Project +;;; +;;;It's the exact same thing as making the initial submission, but you +;;;don't have to include every field, just whatever you want +;;;updated. So, say if you want to add a license field to your +;;;existing entry, you'd just have ((name "foo") (license "bar")) as a +;;;submission. + +((name "robots") + + (category "Games") + (keywords "Game " "robots " "diversion") + + (description "A game/diversion where you construct a robot (using Scheme) " + "then set him loose and watch him explore a world on his own." + "The GNU Robot program is written in Scheme, and implemented " + "using GNU Guile.") + + (location (url "http://www.gnu.org/software/robots/" "GNU Robots homepage")) + + (authors "Jim Hall") + (maintainer (email "Jim Hall" "jhall1@isd.net")) + + (status "GNU Robots has finally been released as version 1.0!!") + + (help-wanted "GNU Robots could really use a port to GTK+, for GNOME. " + "Also, it would be great if someone wrote a GNU Robots " + "code generator, that generated a robot Scheme program " + "based on the user contructing a robot program using " + "little icons that are dropped into place and ordered " + "using special connector wires. This would be a good " + "senior project for a computer science student!") + + (license "GPL")) diff --git a/getopt/Makefile.am b/getopt/Makefile.am new file mode 100644 index 0000000..a5922a3 --- /dev/null +++ b/getopt/Makefile.am @@ -0,0 +1,23 @@ +## +## getopt/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +EXTRA_DIST =\ + getopt1.c\ + getopt.c\ + getopt.h + diff --git a/getopt/getopt.c b/getopt/getopt.c new file mode 100644 index 0000000..4c77c09 --- /dev/null +++ b/getopt/getopt.c @@ -0,0 +1,683 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program 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 2, or (at your option) any + later version. + + This program 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 this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* NOTE!!! AIX requires this to be the first thing in the file. + Do not put ANYTHING before it! */ +#if !defined (__GNUC__) && defined (_AIX) +#pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) +#include +#else +#ifndef _AIX +char *alloca (); +#endif +#endif /* alloca.h */ +#endif /* not __GNUC__ */ + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#undef alloca +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#else /* Not GNU C library. */ +#define __alloca alloca +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +static void +my_bcopy (from, to, size) + const char *from; + char *to; + int size; +{ + int i; + + for (i = 0; i < size; i++) + to[i] = from[i]; +} +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) __alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); + my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + my_bcopy ((char *) temp, + (char *) &argv[first_nonopt + optind - last_nonopt], nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') { + ordering = RETURN_IN_ORDER; + ++optstring; + } else if (optstring[0] == '+') { + ordering = REQUIRE_ORDER; + ++optstring; + } else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') { + if (ordering == PERMUTE) { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) { + if (s - nextchar == strlen (p->name)) { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } else if (pfound == NULL) { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) { + option_index = indfound; + optind++; + if (*s) { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else { + if (opterr) { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } else if (pfound->has_arg == 1) { + if (optind < argc) + optarg = argv[optind++]; + else { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) { + if (opterr) { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') { + if (opterr) { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return '?'; + } + if (temp[1] == ':') { + if (temp[2] == ':') { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') { + optarg = nextchar; + optind++; + } else + optarg = 0; + nextchar = NULL; + } else { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } else if (optind == argc) { + if (opterr) { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, (int *) 0, 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/getopt/getopt.h b/getopt/getopt.h new file mode 100644 index 0000000..4501d46 --- /dev/null +++ b/getopt/getopt.h @@ -0,0 +1,128 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program 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 2, or (at your option) any + later version. + + This program 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 this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + + extern int opterr; + +/* Set to an option character which was unrecognized. */ + + extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + + struct option + { +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; + }; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ + extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ + extern int getopt (); +#endif /* not __GNU_LIBRARY__ */ + extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); + extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ + extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, int long_only); +#else /* not __STDC__ */ + extern int getopt (); + extern int getopt_long (); + extern int getopt_long_only (); + + extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/getopt/getopt1.c b/getopt/getopt1.c new file mode 100644 index 0000000..d0ac2af --- /dev/null +++ b/getopt/getopt1.c @@ -0,0 +1,172 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program 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 2, or (at your option) any + later version. + + This program 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 this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "getopt.h" + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#else +char *getenv (); +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..bdd9f2d --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,27 @@ +## +## include/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +EXTRA_DIST =\ + api.h\ + configs.h\ + map.h\ + main.h\ + grobot.h\ + sign.h\ + userinterface.h + diff --git a/include/api.h b/include/api.h new file mode 100644 index 0000000..e04f27b --- /dev/null +++ b/include/api.h @@ -0,0 +1,43 @@ +/* Robot API for the GNU Robots game */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _API_H +#define _API_H + +#include +#include /* 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); + +SCM api_robot_get_shields (void); +SCM api_robot_get_energy (void); +SCM api_robot_get_score (void); + +#endif /* _API_H */ diff --git a/include/configs.h b/include/configs.h new file mode 100644 index 0000000..b3d5125 --- /dev/null +++ b/include/configs.h @@ -0,0 +1,63 @@ +#ifndef CONFIGS_H +#define CONFIGS_H + +#define TILE_SIZE 16 +#undef USE_MITSHM + +#ifdef PKGLIBDIR +#define MODULE_PATH PKGLIBDIR +#else +#ifdef ABS_TOP_BUILDDIR +#define MODULE_PATH ABS_TOP_BUILDDIR "/lib/.libs" +#else +#define MODULE_PATH "lib/.libs" +#endif /* ABS_TOP_BUILDDIR */ +#endif /* PKGLIBDIR */ + +#ifndef MODULE_PATH_ENV +# define MODULE_PATH_ENV "GNU_ROBOTS_PLUGIN_PATH" +#endif + +#ifndef MAPS_PATH_ENV +# define MAPS_PATH_ENV "GNU_ROBOTS_MAPS_PATH" +#endif + +#ifndef SCRIPTS_PATH_ENV +# define SCRIPTS_PATH_ENV "GNU_ROBOTS_SCRIPTS_PATH" +#endif + +#define MAX_PATH 256 + +/* Defaults */ +#define DEFAULT_MAP "maze.map" +#define DEFAULT_SCRIPT "mapper.scm" + +/* Symbolic constants */ +#define SPACE ' ' +#define FOOD '+' +#define PRIZE '$' +#define WALL '#' +#define BADDIE '@' +#define ROBOT 'R' + +/* Directions */ +#define NORTH 0 +#define EAST 1 +#define SOUTH 2 +#define WEST 3 + +/* For all displays */ +#define SLEEP_TIME 200 /* in milliseconds */ +#define USLEEP_TIME 200000 /* in microseconds */ +#define USLEEP_MULT 16 /* not used yet --jh */ + +#define DEFAULT_ENERGY 1000 +#define DEFAULT_SHIELDS 100 + +#define DEFAULT_MAP_COLUMNS 40 +#define DEFAULT_MAP_ROWS 20 + +#define PKGINFO PACKAGE_NAME " " VERSION +#define COPYRIGHT "Copyright (C) 1998,1999,2000 Jim Hall " + +#endif /* CONFIGS_H */ diff --git a/include/grobot.h b/include/grobot.h new file mode 100644 index 0000000..1ef0031 --- /dev/null +++ b/include/grobot.h @@ -0,0 +1,80 @@ +#ifndef _G_ROBOT_H +#define _G_ROBOT_H +/* MACROS */ + +#include +#include +#include "userinterface.h" +#include "map.h" + +G_BEGIN_DECLS + +extern GType _g_robot_type; + +typedef struct _GRobot GRobot; + +struct _GRobot { + GObject object; + + gint x; + gint y; + gint dir; + glong score; + glong energy; + glong shields; + glong shots; + glong units; + + UserInterface *ui; + Map *map; +}; + +typedef struct _GRobotClass GRobotClass; + +struct _GRobotClass { + GObjectClass parent_class; + + void (*death) (GRobot *robot); +}; + +#define G_TYPE_ROBOT (_g_robot_type) +#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)) +#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)) + +/* some convenient macros */ +#define G_ROBOT_POSITION_X(robot) ((robot)->x) +#define G_ROBOT_POSITION_Y(robot) ((robot)->y) + +/* normal GObject stuff */ +GType g_robot_get_type (void); + +/* Our object functions */ +GRobot* g_robot_new (int x, + int y, + int dir, + long score, + long energy, + long shield, + long units, + long shots, + UserInterface *ui, + Map *map); + +void g_robot_turn (GRobot *robot, gint num_turns); +gboolean g_robot_move (GRobot *robot, gint steps); +gboolean g_robot_smell (GRobot *robot, gchar *str); +gboolean g_robot_feel (GRobot *robot, gchar *str); +gboolean g_robot_look (GRobot *robot, gchar *str); +gboolean g_robot_grab (GRobot *robot); +gboolean g_robot_zap (GRobot *robot); +gboolean g_robot_stop (GRobot *robot); +glong g_robot_get_shields (GRobot *robot); +glong g_robot_get_energy (GRobot *robot); +glong g_robot_get_score (GRobot *robot); + +G_END_DECLS + +#endif diff --git a/include/main.h b/include/main.h new file mode 100644 index 0000000..d09ac65 --- /dev/null +++ b/include/main.h @@ -0,0 +1,10 @@ +/* Functions */ + +#include +#include + +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 new file mode 100644 index 0000000..8818030 --- /dev/null +++ b/include/map.h @@ -0,0 +1,57 @@ +#ifndef _MAP_H +#define _MAP_H +/* MACROS */ + +#include +#include + +G_BEGIN_DECLS + +extern GType _map_type; + +typedef struct +{ + gint num_rows; + gint num_cols; +} MapSize; + +typedef struct _Map Map; + +struct _Map { + GObject object; + + /* The actual Map */ + gint **_map; + MapSize size; + + gint errors; +}; + +typedef struct _MapClass MapClass; + +struct _MapClass { + GObjectClass parent_class; +}; + +#define G_TYPE_MAP (_map_type) +#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)) +#define MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_MAP, MapClass)) + +/* 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) + +/* normal GObject stuff */ +GType map_get_type (void); + +/* Our object functions */ +Map* map_new_from_file (const gchar *map, + gint num_rows, + gint num_cols); + +G_END_DECLS + +#endif diff --git a/include/sign.h b/include/sign.h new file mode 100644 index 0000000..f98c6c1 --- /dev/null +++ b/include/sign.h @@ -0,0 +1,3 @@ +#include + +gint sign (gint n); diff --git a/include/userinterface.h b/include/userinterface.h new file mode 100644 index 0000000..ba5ade9 --- /dev/null +++ b/include/userinterface.h @@ -0,0 +1,211 @@ +/* $Id: userinterface.h,v 1.7 2005/09/06 19:55:40 zeenix Exp $ */ + +/* GNU Robots game engine. This is the header file for user_interface module */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __USER_INTERFACE_H__ +#define __USER_INTERFACE_H__ + +#include +#include "map.h" + +G_BEGIN_DECLS + +extern GType _user_interface_type; + +typedef struct _UserInterface UserInterface; +typedef struct _UserInterfaceClass UserInterfaceClass; + +struct _UserInterfaceClass { + GTypeInterface parent; + + void (* user_interface_add_thing) (UserInterface *ui, + gint x, + gint y, + gint thing); + + void (* user_interface_draw) (UserInterface *ui); + + void (* user_interface_update_status) (UserInterface *ui, + const gchar *s, + glong energy, + glong score, + glong shields); + + void (* user_interface_move_robot) (UserInterface *ui, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields); + + void (* user_interface_robot_smell) (UserInterface *ui, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields); + + void (* user_interface_robot_zap) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + + void (* user_interface_robot_feel) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + + void (* user_interface_robot_grab) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + + void (* user_interface_robot_look) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + +/* routines to get/display data from/to user */ + void (* user_interface_get_string) (UserInterface *ui, + gchar *prompt, + gchar *buff, + gint len); +}; + +#define G_TYPE_USER_INTERFACE (_user_interface_type) +#define G_IS_USER_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_USER_INTERFACE)) +#define G_IS_USER_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_USER_INTERFACE)) +#define USER_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_USER_INTERFACE, UserInterfaceClass)) +#define USER_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_USER_INTERFACE, UserInterface)) +#define USER_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_USER_INTERFACE, UserInterfaceClass)) + +/* normal GObject stuff */ +GType user_interface_get_type (void); + +/* functions we want implemented by the implementers of our interface */ +void user_interface_add_thing (UserInterface *ui, + gint x, + gint y, + gint thing); + +void user_interface_draw (UserInterface *ui); + +void user_interface_update_status (UserInterface *ui, + const gchar *s, + glong energy, + glong score, + glong shields); + +void user_interface_move_robot (UserInterface *ui, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields); + +void user_interface_robot_smell (UserInterface *ui, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields); + +void user_interface_robot_zap (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + +void user_interface_robot_feel (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + +void user_interface_robot_grab (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + +void user_interface_robot_look (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields); + +/* routines to get/display data from/to user */ +void user_interface_get_string (UserInterface *ui, + gchar *prompt, + gchar *buff, + gint len); + +typedef UserInterface * (* UserInterfaceInitFunc) (Map *map, + GType parent_type); +#define USER_INTERFACE_INIT_FUNCTION "user_interface_new" + +G_END_DECLS + +#endif /* __USER_INTERFACE_H__*/ diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..f3d6283 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,48 @@ +## +## lib/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +SUBDIRS = xpm + +if USE_CURSES +CURSES_PLUGIN=libgrobots-curses.la +else +CURSES_PLUGIN= +endif + +if USE_X11 +X11_PLUGIN=libgrobots-x11.la +else +X11_PLUGIN= +endif + +INCLUDES = $(GLIB2_CFLAGS) $(CURSES_CFLAGS) $(X_FLAGS) -I$(top_builddir)/include + +pkglib_LTLIBRARIES = libgrobots-text.la $(CURSES_PLUGIN) $(X11_PLUGIN) + +libgrobots_text_la_SOURCES = textplugin.c +libgrobots_text_la_LDFLAGS = -module -avoid-version +libgrobots_text_la_LIBADD = $(GLIB2_LIBS) + +libgrobots_curses_la_SOURCES = cursesplugin.c +libgrobots_curses_la_LDFLAGS = -module -avoid-version +libgrobots_curses_la_LIBADD = $(GLIB2_LIBS) $(CURSES_LIBS) + +libgrobots_x11_la_SOURCES = x11plugin.c +libgrobots_x11_la_LDFLAGS = -module -avoid-version +libgrobots_x11_la_LIBADD = $(GLIB2_LIBS) $(X_LIBS) + diff --git a/lib/cursesplugin.c b/lib/cursesplugin.c new file mode 100644 index 0000000..a3f08ad --- /dev/null +++ b/lib/cursesplugin.c @@ -0,0 +1,599 @@ +/* $Id: cursesplugin.c,v 1.1 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include "configs.h" +#include "cursesplugin.h" + +enum +{ + ARG_0, + ARG_MAP +}; + +GType _curses_plugin_type = 0; +static GType _parent_type = 0; + +static void curses_plugin_class_init (CursesPluginClass * klass); +static void curses_plugin_init (GObject * object); +static void curses_plugin_interface_init (gpointer g_iface, gpointer iface_data); + +static GObject * curses_plugin_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties); + +static void curses_plugin_finalize (GObject * object); +static void curses_plugin_dispose (GObject * object); + +static void curses_plugin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void curses_plugin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +inline void curses_plugin_update_status (CursesPlugin *curses, + const gchar *s, + glong energy, + glong score, + glong shields); + +static GObjectClass *parent_class = NULL; + +GType +curses_plugin_get_type (void) +{ + if (!_curses_plugin_type) { + static const GTypeInfo object_info = { + sizeof (CursesPluginClass), + NULL, + NULL, + (GClassInitFunc) curses_plugin_class_init, + NULL, + NULL, + sizeof (CursesPlugin), + 0, + (GInstanceInitFunc) curses_plugin_init, + NULL + }; + + static const GInterfaceInfo interface_info = { + (GInterfaceInitFunc) curses_plugin_interface_init, + NULL, + NULL + }; + + _curses_plugin_type = + g_type_register_static (G_TYPE_OBJECT, + "CursesPlugin", + &object_info, + 0); + + g_type_add_interface_static (_curses_plugin_type, + _parent_type, + &interface_info); + } + + return _curses_plugin_type; +} + +static void +curses_plugin_class_init (CursesPluginClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->constructor = curses_plugin_constructor; + gobject_class->set_property = curses_plugin_set_property; + gobject_class->get_property = curses_plugin_get_property; + gobject_class->dispose = curses_plugin_dispose; + gobject_class->finalize = curses_plugin_finalize; + + g_object_class_override_property (gobject_class, ARG_MAP, "map"); +} + +static void +curses_plugin_init (GObject * object) +{ + CursesPlugin *curses = CURSES_PLUGIN (object); + + curses->map = NULL; + curses->map_size = NULL; +} + +static GObject * +curses_plugin_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *object; + CursesPlugin *curses; + + /* Chain up to the parent first */ + object = parent_class->constructor (type, n_construct_properties, construct_properties); + + curses = CURSES_PLUGIN (object); + + /* Initialize curses mode */ + gint color_pair; + + curses->win = initscr (); + + cbreak (); + noecho (); + + scrollok (curses->win, TRUE); /* Put scrolling on */ + setscrreg (LINES - 2, LINES - 1); /* Where to scroll */ + + //clearok (stdscr, TRUE); + nonl (); + intrflush (stdscr, FALSE); + keypad (stdscr, TRUE); + + if (has_colors()) { + start_color(); + + /* + * Simple color assignment, often all we need. + **/ + init_pair (COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); + init_pair (COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); + init_pair (COLOR_RED, COLOR_RED, COLOR_BLACK); + init_pair (COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); + init_pair (COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); + init_pair (COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); + init_pair (COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); + init_pair (COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); + } + + clear (); + refresh (); + curses->errors = 0; + + return object; +} + +static void +curses_plugin_dispose (GObject * object) +{ + CursesPlugin *curses = CURSES_PLUGIN (object); + + if (curses->map != NULL) { + g_object_unref (G_OBJECT (curses->map)); + + if (curses->map_size != NULL) { + g_free (curses->map_size); + } + } + + parent_class->dispose (object); +} + +/* finalize is called when the object has to free its resources */ +static void +curses_plugin_finalize (GObject * object) +{ + /* End curses mode */ + endwin (); + + parent_class->finalize (object); +} + +static void +curses_plugin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + CursesPlugin *curses; + GObject *obj; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (G_IS_CURSES_PLUGIN (object)); + + curses = CURSES_PLUGIN (object); + + switch (prop_id) { + case ARG_MAP: + obj = g_value_get_object (value); + g_return_if_fail (obj != NULL); + + if (curses->map != NULL) { + g_object_unref (curses->map); + } + + curses->map = MAP (g_object_ref (obj)); + + if (curses->map_size != NULL) { + g_free (curses->map_size); + } + + g_object_get (G_OBJECT (curses->map), + "size", &curses->map_size, + NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +curses_plugin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + CursesPlugin *curses; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (G_IS_CURSES_PLUGIN (object)); + + curses = CURSES_PLUGIN (object); + + switch (prop_id) { + case ARG_MAP: + g_value_set_object (value, g_object_ref (G_OBJECT (curses->map))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +UserInterface * +user_interface_new (Map *map, GType parent_type) +{ + g_return_val_if_fail (map != NULL, NULL); + g_return_val_if_fail (_parent_type != 0 || parent_type != 0, NULL); + + if (!_parent_type) { + _parent_type = parent_type; + } + + CursesPlugin *curses = + CURSES_PLUGIN (g_object_new (curses_plugin_get_type (), + "map", map, + NULL)); + if (curses->errors) { + g_object_unref (G_OBJECT (curses)); + return NULL; + } + + return USER_INTERFACE (curses); +} + +inline void curses_plugin_add_thing (CursesPlugin *curses, + gint x, + gint y, + gint thing) +{ + gshort color; + + /* Highlight the unusual chars */ + if (thing == '?') { + standout (); + } + + switch (thing) { + case SPACE: + color = COLOR_PAIR (COLOR_BLACK); + break; + case FOOD: + color = COLOR_PAIR (COLOR_GREEN); + break; + case PRIZE: + color = COLOR_PAIR (COLOR_YELLOW); + break; + case WALL: + color = COLOR_PAIR (COLOR_WHITE); + break; + case BADDIE: + color = COLOR_PAIR (COLOR_RED); + break; + case ROBOT: + color = COLOR_PAIR (COLOR_BLUE); + break; + default: + /* What here? */ + color = COLOR_PAIR (COLOR_BLACK); + } + + mvaddch (y, x, thing | color); + + if (thing == '?') { + standend (); + } + + redrawwin (curses->win); +} + +inline void curses_plugin_draw (CursesPlugin *curses) +{ + gshort color; + gint i, j; + + /* Draw the map for the GNU Robots game. */ + + for (j = 0; j < curses->map_size->num_rows; j++) { + for (i = 0; i < curses->map_size->num_cols; i++) { + /* Special cases */ + + switch (MAP_GET_OBJECT (curses->map, i, j)) { + case '\0': + /* Highlight the unusual chars */ + if (MAP_GET_OBJECT (curses->map, i, j) == '?') { + standout (); + } + color = COLOR_PAIR (COLOR_BLACK); + break; + case SPACE: + color = COLOR_PAIR (COLOR_BLACK); + break; + case FOOD: + color = COLOR_PAIR (COLOR_GREEN); + break; + case PRIZE: + color = COLOR_PAIR (COLOR_YELLOW); + break; + case WALL: + color = COLOR_PAIR (COLOR_WHITE); + break; + case BADDIE: + color = COLOR_PAIR (COLOR_RED); + break; + case ROBOT: + color = COLOR_PAIR (COLOR_BLUE); + break; + default: + /* What here? */ + color = COLOR_PAIR (COLOR_BLACK); + break; + } /* switch */ + + mvaddch (j, i, MAP_GET_OBJECT (curses->map, i, j) | color); + + if (MAP_GET_OBJECT (curses->map, i, j) == '?') { + standend (); + } + } /* for i */ + } /* for j */ + + redrawwin (curses->win); +} + +inline void curses_plugin_move_robot (CursesPlugin *curses, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + /* Clear previous tile */ + + mvaddch (from_y, from_x, ' ' | COLOR_PAIR (COLOR_BLACK)); + + standout (); + switch (cdir) { + case NORTH: + mvaddch (to_y, to_x, '^' | COLOR_PAIR (COLOR_BLUE)); + break; + case EAST: + mvaddch (to_y, to_x, '>' | COLOR_PAIR (COLOR_BLUE)); + break; + case SOUTH: + mvaddch (to_y, to_x, 'v' | COLOR_PAIR (COLOR_BLUE)); + break; + case WEST: + mvaddch (to_y, to_x, '<' | COLOR_PAIR (COLOR_BLUE)); + break; + } + + standend (); + refresh (); + + g_usleep (USLEEP_TIME); +} + +/* function to animate the robot */ +inline void curses_plugin_robot_smell (CursesPlugin *curses, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + curses_plugin_update_status (curses, "robot smells...", energy, score, shields); +} + +inline void curses_plugin_robot_zap (CursesPlugin *curses, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + curses_plugin_update_status (curses, "robot fires his little gun...", energy, score, shields); +} + +inline void curses_plugin_robot_feel (CursesPlugin *curses, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + curses_plugin_update_status (curses, "robot feels for a thing...", energy, score, shields); +} + +inline void curses_plugin_robot_grab (CursesPlugin *curses, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + curses_plugin_update_status (curses, "robot grabs thing...", energy, score, shields); +} + +inline void curses_plugin_robot_look (CursesPlugin *curses, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + curses_plugin_update_status (curses, "robot looks for a thing...", energy, score, shields); +} + +/* function to get data from user */ +inline void curses_plugin_get_string (CursesPlugin *curses, + gchar *prompt, + gchar *buff, + gint len) +{ + mvaddstr (LINES - 2, 0, prompt); + refresh (); + + echo (); + getnstr (buff, len); + noecho (); + + move (LINES - 2, 0); + clrtoeol (); + refresh (); +} + +inline void curses_plugin_update_status (CursesPlugin *curses, + const gchar *s, + glong energy, + glong score, + glong shields) +{ + /* print on mode line (y=LINES-1) */ + mvaddstr (LINES - 1, 1, s); + refresh (); + + /* Sleep, then erase it */ + napms (SLEEP_TIME); + + move (LINES - 1, 1); + clrtoeol (); + refresh (); +} + +static void curses_plugin_interface_init (gpointer g_iface, gpointer iface_data) +{ + UserInterfaceClass *klass = (UserInterfaceClass *)g_iface; + + klass->user_interface_add_thing = (void (*) (UserInterface *ui, + gint x, + gint y, + gint thing)) + curses_plugin_add_thing; + + klass->user_interface_draw = (void (*) (UserInterface *ui)) curses_plugin_draw; + klass->user_interface_update_status = (void (*) (UserInterface *ui, + const gchar *s, + glong energy, + glong score, + glong shields)) + curses_plugin_update_status; + klass->user_interface_move_robot = (void (*) (UserInterface *ui, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields)) + curses_plugin_move_robot; + klass->user_interface_robot_smell = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields)) + curses_plugin_robot_smell; + klass->user_interface_robot_zap = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + curses_plugin_robot_zap; + klass->user_interface_robot_feel = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + curses_plugin_robot_grab; + klass->user_interface_robot_grab = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + curses_plugin_robot_grab; + klass->user_interface_robot_look = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + curses_plugin_robot_look; + klass->user_interface_get_string = (void (*) (UserInterface *ui, + gchar *prompt, + gchar *buff, + gint len)) + curses_plugin_get_string; +} diff --git a/lib/cursesplugin.h b/lib/cursesplugin.h new file mode 100644 index 0000000..7f72d0e --- /dev/null +++ b/lib/cursesplugin.h @@ -0,0 +1,69 @@ +/* $Id: cursesplugin.h,v 1.1 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CURSES_PLUGIN_H__ +#define __CURSES_PLUGIN_H__ + +#include +#include +#include +#include /* for sleep */ +#include "configs.h" +#include "userinterface.h" +#include "map.h" + +G_BEGIN_DECLS + +extern GType _curses_plugin_type; + +typedef struct _CursesPlugin CursesPlugin; + +struct _CursesPlugin { + GObject object; + Map *map; + MapSize *map_size; + gint errors; + + WINDOW *win; +}; + +typedef struct _CursesPluginClass CursesPluginClass; + +struct _CursesPluginClass { + GObjectClass parent_class; +}; + +#define G_TYPE_CURSES_PLUGIN (_curses_plugin_type) +#define G_IS_CURSES_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_CURSES_PLUGIN)) +#define G_IS_CURSES_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_CURSES_PLUGIN)) +#define CURSES_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_CURSES_PLUGIN, CursesPluginClass)) +#define CURSES_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_CURSES_PLUGIN, CursesPlugin)) +#define CURSES_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_CURSES_PLUGIN, CursesPluginClass)) + +/* normal GObject stuff */ +GType curses_plugin_get_type (void); + +CursesPlugin* curses_plugin_new (void); + +G_END_DECLS + +#endif /* __CURSES_PLUGIN_H__*/ diff --git a/lib/textplugin.c b/lib/textplugin.c new file mode 100644 index 0000000..32a5a56 --- /dev/null +++ b/lib/textplugin.c @@ -0,0 +1,444 @@ +/* $Id: textplugin.c,v 1.1 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include "configs.h" +#include "textplugin.h" + +enum +{ + ARG_0, + ARG_MAP +}; + +GType _text_plugin_type = 0; +static GType _parent_type = 0; + +static void text_plugin_class_init (TextPluginClass * klass); +static void text_plugin_init (GObject * object); +static void text_plugin_interface_init (gpointer g_iface, gpointer iface_data); + +static GObject * text_plugin_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties); + +static void text_plugin_finalize (GObject * object); +static void text_plugin_dispose (GObject * object); + +static void text_plugin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void text_plugin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +inline void text_plugin_update_status (TextPlugin *text, + const gchar *s, + glong energy, + glong score, + glong shields); + +static GObjectClass *parent_class = NULL; + +/* For translation of directions to strings */ +static gchar *str_dir[] = { "North", "East", "South", "West" }; + +GType +text_plugin_get_type (void) +{ + if (!_text_plugin_type) { + static const GTypeInfo object_info = { + sizeof (TextPluginClass), + NULL, + NULL, + (GClassInitFunc) text_plugin_class_init, + NULL, + NULL, + sizeof (TextPlugin), + 0, + (GInstanceInitFunc) text_plugin_init, + NULL + }; + + static const GInterfaceInfo interface_info = { + (GInterfaceInitFunc) text_plugin_interface_init, + NULL, + NULL + }; + + _text_plugin_type = + g_type_register_static (G_TYPE_OBJECT, + "TextPlugin", + &object_info, + 0); + + g_type_add_interface_static (_text_plugin_type, + _parent_type, + &interface_info); + } + + return _text_plugin_type; +} + +static void +text_plugin_class_init (TextPluginClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->constructor = text_plugin_constructor; + gobject_class->set_property = text_plugin_set_property; + gobject_class->get_property = text_plugin_get_property; + gobject_class->dispose = text_plugin_dispose; + gobject_class->finalize = text_plugin_finalize; + + g_object_class_override_property (gobject_class, ARG_MAP, "map"); +} + +static void +text_plugin_init (GObject * object) +{ + TextPlugin *text = TEXT_PLUGIN (object); + + text->map = NULL; + text->map_size = NULL; +} + +static GObject * +text_plugin_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *object; + + /* Chain up to the parent first */ + object = parent_class->constructor (type, n_construct_properties, construct_properties); + + g_printf ("GNU Robots starting..\n"); + + return object; +} + +static void +text_plugin_dispose (GObject * object) +{ + TextPlugin *text = TEXT_PLUGIN (object); + + if (text->map != NULL) { + g_object_unref (G_OBJECT (text->map)); + + if (text->map_size != NULL) { + g_free (text->map_size); + } + } + + parent_class->dispose (object); +} + +/* finalize is called when the object has to free its resources */ +static void +text_plugin_finalize (GObject * object) +{ + g_printf ("GNU Robots done.\n"); + + parent_class->finalize (object); +} + +static void +text_plugin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + TextPlugin *text; + GObject *obj; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (G_IS_TEXT_PLUGIN (object)); + + text = TEXT_PLUGIN (object); + + switch (prop_id) { + case ARG_MAP: + obj = g_value_get_object (value); + g_return_if_fail (obj != NULL); + + if (text->map != NULL) { + g_object_unref (text->map); + } + + text->map = MAP (g_object_ref (obj)); + + if (text->map_size != NULL) { + g_free (text->map_size); + } + + g_object_get (G_OBJECT (text->map), + "size", &text->map_size, + NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +text_plugin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + TextPlugin *text; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (G_IS_TEXT_PLUGIN (object)); + + text = TEXT_PLUGIN (object); + + switch (prop_id) { + case ARG_MAP: + g_value_set_object (value, g_object_ref (G_OBJECT (text->map))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +UserInterface * +user_interface_new (Map *map, GType parent_type) +{ + g_return_val_if_fail (map != NULL, NULL); + g_return_val_if_fail (_parent_type != 0 || parent_type != 0, NULL); + + if (!_parent_type) { + _parent_type = parent_type; + } + + TextPlugin *text = + TEXT_PLUGIN (g_object_new (text_plugin_get_type (), + "map", map, + NULL)); + if (text->errors) { + g_object_unref (G_OBJECT (text)); + return NULL; + } + + return USER_INTERFACE (text); +} + +inline void text_plugin_add_thing (TextPlugin *text, + gint x, + gint y, + gint thing) +{ + g_printf ("thing `%c' added at %d,%d\n", thing, x, y); +} + +inline void text_plugin_draw (TextPlugin *text) +{ + gint i, j; + + for (j = 0; j < text->map_size->num_rows; j++) { + for (i = 0; i < text->map_size->num_cols; i++) { + g_printf ("thing `%c' added at %d,%d\n", MAP_GET_OBJECT (text->map, i, j), i, j); + } + } +} + +inline void text_plugin_move_robot (TextPlugin *text, + gint from_x, + gint from_y, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + g_printf ("robot now at %d,%d facing %s\n", x, y, str_dir[cdir]); +} + +/* function to animate the robot */ +inline void text_plugin_robot_smell (TextPlugin *text, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + g_printf ("the robot sniffs..\n"); +} + +inline void text_plugin_robot_zap (TextPlugin *text, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + g_printf ("robot fires gun at space %d,%d\n", x_to, y_to); +} + +inline void text_plugin_robot_feel (TextPlugin *text, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + g_printf ("robot feels space %d,%d\n", x_to, y_to); +} + +inline void text_plugin_robot_grab (TextPlugin *text, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + g_printf ("robot grabs for space %d,%d\n", x_to, y_to); +} + +inline void text_plugin_robot_look (TextPlugin *text, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + g_printf ("robot looks towards %s from %d,%d\n", str_dir[cdir], x, y); +} + +/* function to get data from user */ +inline void text_plugin_get_string (TextPlugin *text, + gchar *prompt, + gchar *buff, + gint len) +{ + fputs (prompt, stdout); + fgets (buff, len, stdin); +} + +inline void text_plugin_update_status (TextPlugin *text, + const gchar *s, + glong energy, + glong score, + glong shields) +{ + puts (s); +} + +static void text_plugin_interface_init (gpointer g_iface, gpointer iface_data) +{ + UserInterfaceClass *klass = (UserInterfaceClass *)g_iface; + + klass->user_interface_add_thing = (void (*) (UserInterface *ui, + gint x, + gint y, + gint thing)) + text_plugin_add_thing; + + klass->user_interface_draw = (void (*) (UserInterface *ui)) text_plugin_draw; + klass->user_interface_update_status = (void (*) (UserInterface *ui, + const gchar *s, + glong energy, + glong score, + glong shields)) + text_plugin_update_status; + klass->user_interface_move_robot = (void (*) (UserInterface *ui, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields)) + text_plugin_move_robot; + klass->user_interface_robot_smell = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields)) + text_plugin_robot_smell; + klass->user_interface_robot_zap = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + text_plugin_robot_zap; + klass->user_interface_robot_feel = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + text_plugin_robot_grab; + klass->user_interface_robot_grab = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + text_plugin_robot_grab; + klass->user_interface_robot_look = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + text_plugin_robot_look; + klass->user_interface_get_string = (void (*) (UserInterface *ui, + gchar *prompt, + gchar *buff, + gint len)) + text_plugin_get_string; +} diff --git a/lib/textplugin.h b/lib/textplugin.h new file mode 100644 index 0000000..fd9eb7b --- /dev/null +++ b/lib/textplugin.h @@ -0,0 +1,63 @@ +/* $Id: textplugin.h,v 1.1 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __TEXT_PLUGIN_H__ +#define __TEXT_PLUING_H__ + +#include +#include "userinterface.h" +#include "map.h" + +G_BEGIN_DECLS + +extern GType _text_plugin_type; + +typedef struct _TextPlugin TextPlugin; + +struct _TextPlugin { + GObject object; + Map *map; + MapSize *map_size; + gint errors; +}; + +typedef struct _TextPluginClass TextPluginClass; + +struct _TextPluginClass { + GObjectClass parent_class; +}; + +#define G_TYPE_TEXT_PLUGIN (_text_plugin_type) +#define G_IS_TEXT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_TEXT_PLUGIN)) +#define G_IS_TEXT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_PLUGIN)) +#define TEXT_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_PLUGIN, TextPluginClass)) +#define TEXT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_TEXT_PLUGIN, TextPlugin)) +#define TEXT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_PLUGIN, TextPluginClass)) + +/* normal GObject stuff */ +GType text_plugin_get_type (void); + +TextPlugin* text_plugin_new (void); + +G_END_DECLS + +#endif /* __TEXT_PLUGIN_H__*/ diff --git a/lib/x11plugin.c b/lib/x11plugin.c new file mode 100644 index 0000000..d40efd9 --- /dev/null +++ b/lib/x11plugin.c @@ -0,0 +1,840 @@ +/* $Id: x11plugin.c,v 1.1 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include "configs.h" +#include "x11plugin.h" + +enum +{ + ARG_0, + ARG_MAP +}; + +GType _x11_plugin_type = 0; +static GType _parent_type = 0; + +static void x11_plugin_class_init (X11PluginClass * klass); +static void x11_plugin_init (GObject * object); +static void x11_plugin_interface_init (gpointer g_iface, gpointer iface_data); + +static GObject * x11_plugin_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties); + +static void x11_plugin_finalize (GObject * object); +static void x11_plugin_dispose (GObject * object); + +static void x11_plugin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void x11_plugin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void put_tile (X11Plugin *x11, XImage *image, gint x, gint y); +static void put_winbuf (X11Plugin *x11); +static void setup_winbuf (X11Plugin *x11); +void create_image (X11Plugin *x11, gchar **data, XImage ** image); +inline void x11_plugin_update_status (X11Plugin *x11, + const gchar *s, + glong energy, + glong score, + glong shields); + +static GObjectClass *parent_class = NULL; + +GType +x11_plugin_get_type (void) +{ + if (!_x11_plugin_type) { + static const GTypeInfo object_info = { + sizeof (X11PluginClass), + NULL, + NULL, + (GClassInitFunc) x11_plugin_class_init, + NULL, + NULL, + sizeof (X11Plugin), + 0, + (GInstanceInitFunc) x11_plugin_init, + NULL + }; + + static const GInterfaceInfo interface_info = { + (GInterfaceInitFunc) x11_plugin_interface_init, + NULL, + NULL + }; + + _x11_plugin_type = + g_type_register_static (G_TYPE_OBJECT, + "X11Plugin", + &object_info, + 0); + + g_type_add_interface_static (_x11_plugin_type, + _parent_type, + &interface_info); + } + + return _x11_plugin_type; +} + +static void +x11_plugin_class_init (X11PluginClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->constructor = x11_plugin_constructor; + gobject_class->set_property = x11_plugin_set_property; + gobject_class->get_property = x11_plugin_get_property; + gobject_class->dispose = x11_plugin_dispose; + gobject_class->finalize = x11_plugin_finalize; + + g_object_class_override_property (gobject_class, ARG_MAP, "map"); +} + +static void +x11_plugin_init (GObject * object) +{ + X11Plugin *x11 = X11_PLUGIN (object); + + x11->map = NULL; + x11->map_size = NULL; +} + +static GObject * +x11_plugin_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + /* Initialize X11 */ + GObject *object; + X11Plugin *x11; +#include "xpm/statusbar.xpm" +#include "xpm/space.xpm" +#include "xpm/food.xpm" +#include "xpm/wall.xpm" +#include "xpm/prize.xpm" +#include "xpm/baddie.xpm" +#include "xpm/robot_north.xpm" +#include "xpm/robot_east.xpm" +#include "xpm/robot_south.xpm" +#include "xpm/robot_west.xpm" +#include "xpm/robot.xpm" + Atom prots[6]; + XClassHint classhint; + XWMHints wmhints; + XGCValues values; + Atom delete_win; + gint x; + + /* Chain up to the parent first */ + object = parent_class->constructor (type, n_construct_properties, construct_properties); + + x11 = X11_PLUGIN (object); + + if ((x11->dpy = XOpenDisplay ("")) == NULL) { + g_printf ("Couldn't open the X Server Display!\n"); + exit (1); /* Exit nicely isn't needed yet, and causes segfault */ + } + + delete_win = XInternAtom (x11->dpy, "WM_DELETE_WINDOW", False); + + x11->win_width = x11->map_size->num_cols * TILE_SIZE; + x11->win_height = x11->map_size->num_rows * TILE_SIZE + 32; + x11->x_win = XCreateSimpleWindow (x11->dpy, DefaultRootWindow (x11->dpy), 0, 0, + x11->win_width, x11->win_height, 0, 0, 0); + + prots[0] = delete_win; + XSetWMProtocols (x11->dpy, x11->x_win, prots, 1); + + XStoreName (x11->dpy, x11->x_win, "GNU Robots"); + + classhint.res_name = "robots"; + classhint.res_class = "Robots"; + XSetClassHint (x11->dpy, x11->x_win, &classhint); + + /* XSetCommand() seems to segfault... */ + + wmhints.input = True; + wmhints.flags = InputHint; + XSetWMHints (x11->dpy, x11->x_win, &wmhints); + + XSelectInput (x11->dpy, x11->x_win, + KeyPressMask | KeyReleaseMask | StructureNotifyMask | FocusChangeMask); + + XMapWindow (x11->dpy, x11->x_win); + + x11->text = XLoadFont (x11->dpy, "-*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*"); + values.font = x11->text; + values.foreground = WhitePixel (x11->dpy, DefaultScreen (x11->dpy)); + + x11->gc = XCreateGC (x11->dpy, x11->x_win, GCFont | GCForeground, &values); + + create_image (x11, statusbar_xpm, &x11->statusbar); + create_image (x11, space_xpm, &x11->space); + create_image (x11, food_xpm, &x11->food); + create_image (x11, wall_xpm, &x11->wall); + create_image (x11, prize_xpm, &x11->prize); + create_image (x11, baddie_xpm, &x11->baddie); + create_image (x11, robot_north_xpm, &x11->robotDirs[0]); + create_image (x11, robot_east_xpm, &x11->robotDirs[1]); + create_image (x11, robot_south_xpm, &x11->robotDirs[2]); + create_image (x11, robot_west_xpm, &x11->robotDirs[3]); + create_image (x11, robot_xpm, &x11->robotPix); + + setup_winbuf (x11); + + //update_status ("Welcome to GNU Robots"); + x11->errors = 0; + + return object; +} + +static void +x11_plugin_dispose (GObject * object) +{ + X11Plugin *x11 = X11_PLUGIN (object); + + if (x11->map != NULL) { + g_object_unref (G_OBJECT (x11->map)); + + if (x11->map_size != NULL) { + g_free (x11->map_size); + } + } + + parent_class->dispose (object); +} + +/* finalize is called when the object has to free its resources */ +static void +x11_plugin_finalize (GObject * object) +{ + X11Plugin *x11 = X11_PLUGIN (object); + + /* End X11 mode */ +#ifdef USE_MITSHM + if (use_mitshm) { + XShmDetach (x11->dpy, &shm_info); + if (shm_info.shmaddr) + shmdt (shm_info.shmaddr); + if (shm_info.shmid >= 0) + shmctl (shm_info.shmid, IPC_RMID, 0); + } +#endif + XDestroyWindow (x11->dpy, x11->x_win); + XUnloadFont (x11->dpy, x11->text); + + parent_class->finalize (object); +} + +static void +x11_plugin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + X11Plugin *x11; + GObject *obj; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (G_IS_X11_PLUGIN (object)); + + x11 = X11_PLUGIN (object); + + switch (prop_id) { + case ARG_MAP: + obj = g_value_get_object (value); + g_return_if_fail (obj != NULL); + + if (x11->map != NULL) { + g_object_unref (x11->map); + } + + x11->map = MAP (g_object_ref (obj)); + + if (x11->map_size != NULL) { + g_free (x11->map_size); + } + + g_object_get (G_OBJECT (x11->map), + "size", &x11->map_size, + NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +x11_plugin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + X11Plugin *x11; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (G_IS_X11_PLUGIN (object)); + + x11 = X11_PLUGIN (object); + + switch (prop_id) { + case ARG_MAP: + g_value_set_object (value, g_object_ref (G_OBJECT (x11->map))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +UserInterface * +user_interface_new (Map *map, GType parent_type) +{ + g_return_val_if_fail (map != NULL, NULL); + g_return_val_if_fail (_parent_type != 0 || parent_type != 0, NULL); + + if (!_parent_type) { + _parent_type = parent_type; + } + + X11Plugin *x11 = + X11_PLUGIN (g_object_new (x11_plugin_get_type (), + "map", map, + NULL)); + if (x11->errors) { + g_object_unref (G_OBJECT (x11)); + return NULL; + } + + return USER_INTERFACE (x11); +} + +/* note that hook_delete_thing(x,y) is the same as + hook_add_thing(x,y,space) */ +inline void x11_plugin_add_thing (X11Plugin *x11, + gint x, + gint y, + gint thing) +{ + gint w_x, w_y; + + w_x = x * TILE_SIZE; + w_y = y * TILE_SIZE; + + switch (thing) { + case SPACE: + put_tile (x11, x11->space, w_x, w_y); + break; + case FOOD: + put_tile (x11, x11->food, w_x, w_y); + break; + case PRIZE: + put_tile (x11, x11->prize, w_x, w_y); + break; + case WALL: + put_tile (x11, x11->wall, w_x, w_y); + break; + case BADDIE: + put_tile (x11, x11->baddie, w_x, w_y); + break; + case ROBOT: + put_tile (x11, x11->robotPix, w_x, w_y); + break; + default: + put_tile (x11, x11->wall, w_x, w_y); + break; + } + + put_winbuf (x11); + XFlush (x11->dpy); +} + +inline void x11_plugin_draw (X11Plugin *x11) +{ + gint i, j; + + /* Draw the map for the GNU Robots game. */ + for (j = 0; j < x11->map_size->num_rows; j++) { + for (i = 0; i < x11->map_size->num_cols; i++) { + /* Special cases */ + switch (MAP_GET_OBJECT (x11->map, i, j)) { + /* Add something for the ROBOT?? */ + case '\0': + put_tile (x11, x11->wall, i * TILE_SIZE, j * TILE_SIZE); + break; + case SPACE: + put_tile (x11, x11->space, i * TILE_SIZE, j * TILE_SIZE); + break; + case FOOD: + put_tile (x11, x11->food, i * TILE_SIZE, j * TILE_SIZE); + break; + case PRIZE: + put_tile (x11, x11->prize, i * TILE_SIZE, j * TILE_SIZE); + break; + case WALL: + put_tile (x11, x11->wall, i * TILE_SIZE, j * TILE_SIZE); + break; + case BADDIE: + put_tile (x11, x11->baddie, i * TILE_SIZE, j * TILE_SIZE); + break; + case ROBOT: + put_tile (x11, x11->robotPix, i * TILE_SIZE, j * TILE_SIZE); + break; + default: + put_tile (x11, x11->wall, i * TILE_SIZE, j * TILE_SIZE); + break; + } /* switch */ + } /* for i */ + } /* for j */ + + put_winbuf (x11); + XSync (x11->dpy, FALSE); +} + +inline void x11_plugin_move_robot (X11Plugin *x11, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + 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; + Bool ok; + + g_assert (distance <= 1); + + x11_plugin_update_status (x11, "robot moves..", energy, score, shields); + + /* Check if robot is moving withing a single box */ + if (distance == 0) { + put_tile (x11, x11->space, from_x * TILE_SIZE, from_y * TILE_SIZE); + put_tile (x11, x11->robotDirs[cdir], to_x * TILE_SIZE, to_y * TILE_SIZE); + + put_winbuf (x11); + XSync (x11->dpy, False); + g_usleep (USLEEP_TIME / 16); + + return; + } + + 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"); + } + + while (1) { + put_tile (x11, x11->space, from_x, from_y); + put_tile (x11, x11->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; + } + put_winbuf (x11); + XSync (x11->dpy, False); + g_usleep (USLEEP_TIME / 16); + if (!ok) + break; + } + + g_usleep (USLEEP_TIME); +} + +/* hooks to animate the robot */ +inline void x11_plugin_robot_smell (X11Plugin *x11, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + /* If we want to change the pic, do it here */ + x11_plugin_update_status (x11, "robot sniffs...", energy, score, shields); + g_usleep (USLEEP_TIME); +} + +inline void x11_plugin_robot_zap (X11Plugin *x11, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + x11_plugin_update_status (x11, "robot fires his little gun...", energy, score, shields); + g_usleep (USLEEP_TIME); +} + +inline void x11_plugin_robot_feel (X11Plugin *x11, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + x11_plugin_update_status (x11, "robot feels for a thing...", energy, score, shields); + g_usleep (USLEEP_TIME); +} + +inline void x11_plugin_robot_grab (X11Plugin *x11, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + x11_plugin_update_status (x11, "robot grabs thing...", energy, score, shields); + g_usleep (USLEEP_TIME); +} + +inline void x11_plugin_robot_look (X11Plugin *x11, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + x11_plugin_update_status (x11, "robot looks for a thing...", energy, score, shields); + g_usleep (USLEEP_TIME); +} + +/* hooks to get/display data from/to user */ +inline void x11_plugin_get_string (X11Plugin *x11, + gchar *prompt, + gchar *buff, + gint len) +{ + /* You want me to write a text-box using xlib? + * You got to be kidding me + * */ + fputs (prompt, stdout); + fgets (buff, len, stdin); +} + +inline void x11_update_status (X11Plugin *x11, + const gchar *s, + glong energy, + glong score, + glong shields) +{ + gchar status[20]; + gint x = 0; + + while (x < x11->win_width) { + XPutImage (x11->dpy, x11->win_buf, x11->buf_gc, x11->statusbar, 0, 0, x, x11->map_size->num_rows * TILE_SIZE, 96, + 32); + x = x + 96; + } + + XDrawString (x11->dpy, x11->win_buf, x11->gc, 3, x11->map_size->num_rows * TILE_SIZE + 16, s, strlen (s)); + + if (energy > -1) { + g_sprintf (status, "Robot Energy: %3d", energy); + XDrawString (x11->dpy, x11->win_buf, x11->gc, 240, x11->map_size->num_rows * TILE_SIZE + 12, status, + strlen (status)); + } + + if (score > -1) { + g_sprintf (status, "Robot Score: %3d", score); + XDrawString (x11->dpy, x11->win_buf, x11->gc, 240, x11->map_size->num_rows * TILE_SIZE + 25, status, + strlen (status)); + } + + if (shields > -1) { + g_sprintf (status, "Robot Shields: %3d", shields); + XDrawString (x11->dpy, x11->win_buf, x11->gc, 480, x11->map_size->num_rows * TILE_SIZE + 12, status, + strlen (status)); + } +} + +inline void x11_plugin_update_status (X11Plugin *x11, + const gchar *s, + glong energy, + glong score, + glong shields) +{ + gint x; + XEvent ev; + + x11_update_status (x11, s, energy, score, shields); + + while (XPending (x11->dpy)) { + XNextEvent (x11->dpy, &ev); + + switch (ev.type) { + case KeyPress: + case KeyRelease: + switch (XKeycodeToKeysym (x11->dpy, ev.xkey.keycode, 0)) { + case XK_Escape: + exit (0); + break; + } + } + } + + put_winbuf (x11); +} + +#ifdef USE_MITSHM +inline gint +shm_error_handler (X11Plugin *x11, Display * d, XErrorEvent * e) +{ + x11->use_mitshm = 0; + return 0; +} +#endif + +static void +setup_winbuf (X11Plugin *x11) +{ + gint major, minor; + Bool shared; + XVisualInfo *matches; + XVisualInfo plate; + gint count; + guint depth; + XGCValues values; + Visual *vis; + + vis = DefaultVisualOfScreen (DefaultScreenOfDisplay (x11->dpy)); + plate.visualid = XVisualIDFromVisual (vis); + matches = XGetVisualInfo (x11->dpy, VisualIDMask, &plate, &count); + depth = matches[0].depth; + +#ifdef USE_MITSHM + x11->use_mitshm = 1; + x11->shm_info.shmid = shmget (IPC_PRIVATE, win_height * win_width * depth, + IPC_CREAT | 0777); + if (x11->shm_info.shmid < 0) { + g_fprintf (stderr, "shmget failed, looks like I'll have to use XPutImage\n"); + x11->use_mitshm = 0; + } else { + x11->shm_info.shmaddr = (gchar *) shmat (x11->shm_info.shmid, 0, 0); + + if (!x11->shm_info.shmaddr) { + g_fprintf (stderr, "shmat failed, looks like I'll have to use XPutImage\n"); + shmctl (x11->shm_info.shmid, IPC_RMID, 0); + use_mitshm = 0; + } else { + XErrorHandler error_handler = XSetErrorHandler (shm_error_handler); + + x11->win_bufi = XShmCreateImage (x11->dpy, vis, depth, ZPixmap, x11->shm_info.shmaddr, + &x11->shm_info, x11->win_width, x11->win_height); + x11->shm_info.readOnly = False; + XShmAttach (x11->dpy, &x11->shm_info); + win_buf = XShmCreatePixmap (x11->dpy, x11->x_win, x11->shm_info.shmaddr, &x11->shm_info, + x11->win_width, x11->win_height, depth); + XSync (x11->dpy, False); + (void) XSetErrorHandler (error_handler); + if (!use_mitshm) { + p_fprintf (stderr, + "XShmAttach failed, looks like I'll have to use XPutImage\n"); + XFreePixmap (x11->dpy, x11->win_buf); + XDestroyImage (x11->win_bufi); + shmdt (x11->shm_info.shmaddr); + shmctl (x11->shm_info.shmid, IPC_RMID, 0); + } + } + } + + if (!x11->use_mitshm) { +#endif /* USE_MITSHM */ + x11->win_buf = XCreatePixmap (x11->dpy, x11->x_win, x11->win_width, x11->win_height, depth); +#ifdef USE_MITSHM + } else { + g_printf ("Using MIT Shared Memory Pixmaps. Good.\n", major, minor); + } +#endif + + values.font = x11->text; + values.foreground = WhitePixel (x11->dpy, DefaultScreen (x11->dpy)); + + x11->buf_gc = XCreateGC (x11->dpy, x11->win_buf, GCFont | GCForeground, &values); +} + +void +create_image (X11Plugin *x11, gchar **data, XImage ** image) +{ + XpmAttributes a; + gint err; + + a.valuemask = XpmCloseness | XpmAllocCloseColors; + a.closeness = 40000; /* the xpm manual suggests 40000 */ + a.alloc_close_colors = 1; /* allocate the colours chosen */ + err = XpmCreateImageFromData (x11->dpy, data, image, NULL, &a); + if (err != 0) { + g_fprintf (stderr, "Cannot create image from xpm: %s\n", + XpmGetErrorString (err)); + exit (1); + } +} + +static void +put_tile (X11Plugin *x11, XImage *image, gint x, gint y) +{ + XPutImage (x11->dpy, x11->win_buf, x11->gc, image, 0, 0, x, y, TILE_SIZE, TILE_SIZE); +} + +static void put_winbuf (X11Plugin *x11) +{ +#ifdef USE_MITSHM + if (use_mitshm) + XShmPutImage (x11->dpy, x11->x_win, x11->gc, x11->win_bufi, 0, 0, 0, 0, x11->win_width, x11->win_height, + False); + else +#endif + XCopyArea (x11->dpy, x11->win_buf, x11->x_win, x11->gc, 0, 0, x11->win_width, x11->win_height, 0, 0); + + XSync (x11->dpy, 0); +} + +static void x11_plugin_interface_init (gpointer g_iface, gpointer iface_data) +{ + UserInterfaceClass *klass = (UserInterfaceClass *)g_iface; + + klass->user_interface_add_thing = (void (*) (UserInterface *ui, + gint x, + gint y, + gint thing)) + x11_plugin_add_thing; + + klass->user_interface_draw = (void (*) (UserInterface *ui)) x11_plugin_draw; + klass->user_interface_update_status = (void (*) (UserInterface *ui, + const gchar *s, + glong energy, + glong score, + glong shields)) + x11_plugin_update_status; + klass->user_interface_move_robot = (void (*) (UserInterface *ui, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields)) + x11_plugin_move_robot; + klass->user_interface_robot_smell = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields)) + x11_plugin_robot_smell; + klass->user_interface_robot_zap = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + x11_plugin_robot_zap; + klass->user_interface_robot_feel = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + x11_plugin_robot_grab; + klass->user_interface_robot_grab = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + x11_plugin_robot_grab; + klass->user_interface_robot_look = (void (*) (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields)) + x11_plugin_robot_look; + klass->user_interface_get_string = (void (*) (UserInterface *ui, + gchar *prompt, + gchar *buff, + gint len)) + x11_plugin_get_string; +} diff --git a/lib/x11plugin.h b/lib/x11plugin.h new file mode 100644 index 0000000..0c39fa2 --- /dev/null +++ b/lib/x11plugin.h @@ -0,0 +1,102 @@ +/* $Id: x11plugin.h,v 1.1 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __X11_PLUGIN_H__ +#define __X11_PLUGIN_H__ + +#include +#include +#include +#include + +#include +#include "userinterface.h" +#include "map.h" + +G_BEGIN_DECLS + +extern GType _x11_plugin_type; + +typedef struct _X11Plugin X11Plugin; + +struct _X11Plugin { + GObject object; + Map *map; + MapSize *map_size; + gint errors; + + Display *dpy; + Window x_win; + GC gc; + GC buf_gc; + Font text; + +#ifdef USE_MITSHM +#include +#include +#include + + XShmSegmentInfo + shm_info; + guchar use_mitshm = 1; +#endif + + gint win_width; + gint win_height; + + Pixmap win_buf; + + XImage *win_bufi; + XImage *statusbar; + XImage *space; + XImage *food; + XImage *wall; + XImage *prize; + XImage *baddie; + XImage *robotDirs[4]; + XImage *robotPix; +}; + +typedef struct _X11PluginClass X11PluginClass; + +struct _X11PluginClass { + GObjectClass parent_class; +}; + +#define G_TYPE_X11_PLUGIN (_x11_plugin_type) +#define G_IS_X11_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_X11_PLUGIN)) +#define G_IS_X11_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_X11_PLUGIN)) +#define X11_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_X11_PLUGIN, X11PluginClass)) +#define X11_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_X11_PLUGIN, X11Plugin)) +#define X11_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_X11_PLUGIN, X11PluginClass)) + +/* normal GObject stuff */ +GType x11_plugin_get_type (void); + +X11Plugin* x11_plugin_new (void); + +/* SYMBOLIC CONSTANTS */ +#define dist(f_x, f_y, t_x, t_y) (abs((f_x)-(t_x))+abs((f_y)-(t_y))) + +G_END_DECLS + +#endif /* __X11_PLUGIN_H__*/ diff --git a/lib/xpm/Makefile.am b/lib/xpm/Makefile.am new file mode 100644 index 0000000..60cb556 --- /dev/null +++ b/lib/xpm/Makefile.am @@ -0,0 +1,31 @@ +## +## xpm/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +EXTRA_DIST =\ + baddie.xpm\ + food.xpm\ + prize.xpm\ + robot_east.xpm\ + robot_north.xpm\ + robot_south.xpm\ + robot_west.xpm\ + robot.xpm\ + space.xpm\ + statusbar.xpm\ + wall.xpm + diff --git a/lib/xpm/baddie.xpm b/lib/xpm/baddie.xpm new file mode 100644 index 0000000..60c1c6d --- /dev/null +++ b/lib/xpm/baddie.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *baddie_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #ffffff", +"# c #990033", +"a c #c0c0c0", +"b c #808080", +"c c #ffffff", +/* pixels */ +"................", +"................", +"......#...#.....", +".......###......", +".....a#####a....", +".....b#c###b....", +"....b#c###b#b...", +"....##cbab###...", +"...#####a#####..", +"....####a####...", +"...#b###a###b#..", +".....###a###....", +"....#a##a##a#...", +".......#b#......", +"................", +"................" +}; diff --git a/lib/xpm/food.xpm b/lib/xpm/food.xpm new file mode 100644 index 0000000..34558d3 --- /dev/null +++ b/lib/xpm/food.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char *food_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 4 1", +/* colors */ +". c #f83000", +"# c #c8ccc8", +"a c #989898", +"b c #f8fcf8", +/* pixels */ +"bbbbbbbbbbbbbbbb", +"bbbbbbbbbbbbbbbb", +"bbbbbbbbbbbbbbbb", +"bbb#bbbbbb#b#abb", +"bbbbbbbbbbbbbabb", +"bbbbbbbbbbbbbabb", +"bbbbbbbb.bbbbabb", +"bbbbbbbb.bbbbabb", +"bbbbbb.....bbabb", +"bbbbbbbb.bbbbabb", +"bbbbbbbb.bbbbabb", +"bbbbbbbbbbbbbabb", +"bbbbbbbbbbbbbabb", +"bbbaaaaaaaaaaabb", +"bbbbbbbbbbbbbbbb", +"bbbbbbbbbbbbbbbb" +}; diff --git a/lib/xpm/prize.xpm b/lib/xpm/prize.xpm new file mode 100644 index 0000000..96c4dad --- /dev/null +++ b/lib/xpm/prize.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char *prize_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 8 1", +/* colors */ +". c #303098", +"# c #003098", +"a c #989898", +"b c #c8ccc8", +"c c #606460", +"d c #f8fcf8", +"e c #989800", +"f c #000000", +/* pixels */ +"dddddddddddddddd", +"dddddddddddddddd", +"dddddddddddddddd", +"dddddddddddddddd", +"dddd........dddd", +"dddd#deeeed.dddd", +"dddd#dedded#dddd", +"dddd#dedddd#dddd", +"dddd##.#.###dddd", +"dddd##c#cc##dddd", +"dddd##c#cc#.dddd", +"dddd.#cccc#.dddd", +"dddddddddddddddd", +"dddddddddddddddd", +"dddddddddddddddd", +"dddddddddddddddd" +}; diff --git a/lib/xpm/robot.xpm b/lib/xpm/robot.xpm new file mode 100644 index 0000000..fea60e2 --- /dev/null +++ b/lib/xpm/robot.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *robot_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #bfbfbf", +"# c #000000", +"a c #808080", +"b c #c0c0c0", +"c c #ffffff", +/* pixels */ +"................", +"................", +"................", +"..####...####...", +".#bccb#.#bccb#..", +"#bccccb#bccccb#.", +"#cccccc#cccccc#a", +"#cc##cc#cc##cc#a", +"#cc##cc#cc##cc#a", +"#bccccb#bccccb#a", +".#bccb#a#bccb#aa", +"..####aa.####aa.", +"...aaaa...aaaa..", +"................", +"................", +"................" +}; diff --git a/lib/xpm/robot_east.xpm b/lib/xpm/robot_east.xpm new file mode 100644 index 0000000..14da711 --- /dev/null +++ b/lib/xpm/robot_east.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *robot_east_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #bfbfbf", +"# c #000000", +"a c #808080", +"b c #c0c0c0", +"c c #ffffff", +/* pixels */ +"................", +"................", +"................", +"..####...####...", +".#bccb#.#bccb#..", +"#bccccb#bccccb#.", +"#cccccc#cccccc#a", +"#ccc##c#ccc##c#a", +"#ccc##c#ccc##c#a", +"#bccccb#bccccb#a", +".#bccb#a#bccb#aa", +"..####aa.####aa.", +"...aaaa...aaaa..", +"................", +"................", +"................" +}; diff --git a/lib/xpm/robot_north.xpm b/lib/xpm/robot_north.xpm new file mode 100644 index 0000000..ec7284d --- /dev/null +++ b/lib/xpm/robot_north.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *robot_north_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #bfbfbf", +"# c #000000", +"a c #808080", +"b c #c0c0c0", +"c c #ffffff", +/* pixels */ +"................", +"................", +"................", +"..####...####...", +".#bccb#.#bccb#..", +"#bc##cb#bc##cb#.", +"#cc##cc#cc##cc#a", +"#cccccc#cccccc#a", +"#cccccc#cccccc#a", +"#bccccb#bccccb#a", +".#bccb#a#bccb#aa", +"..####aa.####aa.", +"...aaaa...aaaa..", +"................", +"................", +"................" +}; diff --git a/lib/xpm/robot_south.xpm b/lib/xpm/robot_south.xpm new file mode 100644 index 0000000..b0ffc68 --- /dev/null +++ b/lib/xpm/robot_south.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *robot_south_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #bfbfbf", +"# c #000000", +"a c #808080", +"b c #c0c0c0", +"c c #ffffff", +/* pixels */ +"................", +"................", +"................", +"..####...####...", +".#bccb#.#bccb#..", +"#bccccb#bccccb#.", +"#cccccc#cccccc#a", +"#cccccc#cccccc#a", +"#cc##cc#cc##cc#a", +"#bc##cb#bc##cb#a", +".#bccb#a#bccb#aa", +"..####aa.####aa.", +"...aaaa...aaaa..", +"................", +"................", +"................" +}; diff --git a/lib/xpm/robot_west.xpm b/lib/xpm/robot_west.xpm new file mode 100644 index 0000000..4a5a2af --- /dev/null +++ b/lib/xpm/robot_west.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char *robot_west_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 5 1", +/* colors */ +". c #bfbfbf", +"# c #000000", +"a c #808080", +"b c #c0c0c0", +"c c #ffffff", +/* pixels */ +"................", +"................", +"................", +"..####...####...", +".#bccb#.#bccb#..", +"#bccccb#bccccb#.", +"#cccccc#cccccc#a", +"#c##ccc#c##ccc#a", +"#c##ccc#c##ccc#a", +"#bccccb#bccccb#a", +".#bccb#a#bccb#aa", +"..####aa.####aa.", +"...aaaa...aaaa..", +"................", +"................", +"................" +}; diff --git a/lib/xpm/space.xpm b/lib/xpm/space.xpm new file mode 100644 index 0000000..bce27fb --- /dev/null +++ b/lib/xpm/space.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static char *space_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 3 1", +/* colors */ +". c #989898", +"# c #c8ccc8", +"a c #f8fcf8", +/* pixels */ +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa", +"aaaaaaaaaaaaaaaa" +}; diff --git a/lib/xpm/statusbar.xpm b/lib/xpm/statusbar.xpm new file mode 100644 index 0000000..89f5a05 --- /dev/null +++ b/lib/xpm/statusbar.xpm @@ -0,0 +1,58 @@ +/* XPM */ +static char * statusbar_xpm[] = { +"96 32 23 1", +" c None", +". c #323232", +"+ c #343434", +"@ c #363636", +"# c #313131", +"$ c #373737", +"% c #3A3A3A", +"& c #393939", +"* c #2F2F2F", +"= c #262626", +"- c #282828", +"; c #2A2A2A", +"> c #2D2D2D", +", c #1B1B1B", +"' c #1D1D1D", +") c #222222", +"! c #242424", +"~ c #202020", +"{ c #2B2B2B", +"] c #3D3D3D", +"^ c #3C3C3C", +"/ c #3F3F3F", +"( c #191919", +".++@@++.##.$%&*=-;>#..>-,')!=)','~=>#*>;-;{>>*>*#......#=;*$&${~)!-;>>>>**#+++*{{>*#.++..+@&&&$@", +"++@@@.#**>#@&&#{-->*+.>>'~)!!~',''=>##>{-;{>>***##..++..;{*@&@>=!!-{>>>>>**.@+#>>>>#...+#.@$$$$@", +"++@@+#>>>{>+&&+*--{>.+.*!!!!)~''''!{*#*>;;{>>*#....+@++@>>>#@+*;!=;{>>>>>>*#@@+#{>>*##..#.+@@@@+", +"+@@@.*{---{#@&$+;-;>#+++;;-=)~''''!;*#*>{{{>>#.+..++@$$$*>{>*#>{=-;>>>>>>>>*.$@@{>>>>##.##...+.+", +".@@@+*;===;>.@&${;;{*.@@>>{-=)~~'~);>###>>>>>#.@..++@$&&.>;-{>{-=-{>>*>>>{;>#+@@>{{>>>*#***####.", +"#.@$+>;!!=-;>.@&*>-->#+@##*>{-!)~~)->#..*>>>>#+@++++@$&&@#;=-;-!!-{>**>>{;=-{*.+{{{;{>*#**>>>*##", +"*.@@@*{====-{>+$.>;-;>#..++.#>-!))!->*#.##>>>#+$+++@@@$&%+>;--!)!={>##*>{-))=>*#>{;-;>>**>>>>>**", +">#@$@#{-====->.$@#;!->*#.+@$.>;!!!!=>*...#>>>*+$+@++@$$&]$#{;-!~)={>##**{=~~);>>{{;-;{>**>>{{>>*", +";>+&&.>;)-;;-;>.+.#>;;>*##.$%&#;!!)={>##+..*>>.+++$+..&%$&&.;!!!=={*..*>{-)''~!;=-;=!!{*#*>>>>>*", +"-{*+&@*{!={;;;>#+.#>{;>*#**@&&+>-=!={>*#...#>*#.+@@+.#@&&&&+>=!==-;>.+#*{;!~')!-=;;-!!;>.#*>>>**", +"{;;*@&+>=-;;;{>*++.*{{{>**>.$&@.{;--{>>>.+.#**#.+@@+#*#.&^^&*;--;;{>#+++>;=)~~!=-;{;!)-{####*###", +">=!;+%$.=-;;{{>*.++#>{>>>>>*+&&&>{{;>>>>.++.***#@@@+#*>>&^]%@>;-{{{>#@@${;=!))!!-{>;!!!;##....##", +">-~-.&&+;;-;{>>>#.++#>>{>>>>#@&&+#>>>>>>..++####+@@@.*{=@&/]&#{;>>{>#++@;;-=!!)!=;>;=)!-*#++@++.", +">-)-*@$@{;-;>>>>*.@@.*>>{{{>*+$&@+#*>*>>#.+@.##.++@@@#;!*$]/&.>{>>>>#.#*=-==!!!)!-{{=!!->#+@$@+#", +">{;;>#@@>;-->>>{>#@$@#>>;{>>*#@$&@.#***>>.+@+.#.+++$&.>!-#%/%+>{>>>*#*>;!!====))~={{-==;>*+$&@+#", +"#>>{{>.+>;=;>>>{>*@&@.*>>{>>>*.@%&+.##*>>#+@+.....@&&+>!!>&]%@>>>>>##>;=))====!)'!;{-=-;>>+$&@.#", +"=-;>*#**{{-;>>>{>>*+&&.{{{{{>*#+@@$$@+#*>>*+$$+.##+$&&#;=;>+&$.>>>*#.*-~''~=;-!~'~!=--;-{>#@&&@.", +"-=-{#..*>{;;>>>>{;>.$&.>{{{{>*#.@$$$$@..>>*.@$@...+&%&+>-;>.@@.>*>>*.#{!''~=--=~~~)!--;;{>*+&&$+", +"{=!;#$@.>{;{>**>;-{*$$+#>>>>>>**@@$&$$$@>>*.@$$@.#.$%%$.;;>#.+#**{->+@>=''')-;-=~)!=--;->>*+&&&$", +">=~-#&&+>>{{>*##--->.$@.>>>>>>>*+@$&&&&&##*#+@$@.##+&&&+{{>*#.#**;)-#@#;''')=;;-)!!=--;;{>>#@&&$", +">-)=*$&&*>>>*#.#;-!;*++.*>>>>>>>.+@$&&%%+.*#.@$$.*>*+$@.>>>*##*>>!'!>.#>~'''!-;{===--;;;>>{>.$$&", +">;==>.$&+*>>*...>;!={*##>*>>>>>>#.+@$&&%&+#*#+$&+*>>*.#**>*#.#>;=~''=>**!)'')-;;;;-;;;{{*>{{>.++", +">>{-->@^@.*>#...#{=!->*>>>>>>>>>##.+@&&%&$#**+$$&.>{>**>**.++#{-''','!>.;)''~=-{{{{;{{>>*>;-{>*#", +">>>;!>+^$+***.+.+>=)={>>>>>>***>**#.@$&&^&#**.@&%@>{>>>>*#+$@#{!(,'(('{.{=~''!-;>>{{{{>>.>-=-{>>", +">>>{;>*.@+####.@.#>=~~->;{>>>*>*>>*#.+$&&&@#>>#@$$.>{{>*+##+&$*-,'''(')-;-!)');>*>>>>>*##*{-=-;>", +">>>{{{>#++.####.+.#;!)->{>>>>**>>>**.+@$&&$#>{>+&$+#>>*..##+&$#{''~','~=-;=)~!->>*>****#.*>-=-;{", +">>>>{{>*++..***>+@+>-!=;{{>>****>>>*#.++&&$.>;>*$$+#*>#+#**+$&@#)))'''~);;-!))->>*##..+..*>-=-;;", +">>>>>{{>..+.#*>;+@$.>==-{{>>>*#*>>>>**..$&&+>-;{++.#**#.*>>#$&$@--!)~'~);;;-=!-;>*#+@@@++#>-==-;", +"{>**>{{{#.++.>-=*+$$#;=={{{>>**#>>>>>**#+$&+>-=->>**>>*#>>>*.$&&>>;=!!))-{>{---;>*.+@$$&@.>;-=-;", +"{>*#>>{{>#+@.>=~-*$&+>-!;;{{>>*#>>>>>>>**+$@*;==;{>>>>>*>{>>*+$&.*>{;;=!;{>>>{;{>**.@$&&&+*{---;", +"->*#*>{{>*+@+>!'~;@&@*-=;;;;>>**>>>>>>>>{#@@*;==-;>>>>>#>>{{>*+&$+>>>>{=-{***>{>>>**.$&^&$#>---;", +"-{*#*>{{;>+@+>!'(-.&$*;=---;{>***>>>>>>>-*+@#{=!-{>>>>*#>{;-;>.$&@#***>{->*.*>>>*>>>#@&]&$.>;-;{"}; diff --git a/lib/xpm/wall.xpm b/lib/xpm/wall.xpm new file mode 100644 index 0000000..48b6a7c --- /dev/null +++ b/lib/xpm/wall.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static char *wall_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 16 16 3 1", +/* colors */ +". c #cccccc", +"# c #993300", +"a c #cc0033", +/* pixels */ +"...............#", +".aaaaaaaaaaaaaa#", +".aaaaaaaaaaaaaa#", +".aa##########aa#", +".aa#aaaaaaaa.aa#", +".aa#aaaaaaaa.aa#", +".aa#aa...#aa.aa#", +".aa#aa.aa#aa.aa#", +".aa#aa.aa#aa.aa#", +".aa#aa####aa.aa#", +".aa#aaaaaaaa.aa#", +".aa#aaaaaaaa.aa#", +".aa#.........aa#", +".aaaaaaaaaaaaaa#", +".aaaaaaaaaaaaaa#", +"################" +}; diff --git a/maps/Makefile.am b/maps/Makefile.am new file mode 100644 index 0000000..79c4634 --- /dev/null +++ b/maps/Makefile.am @@ -0,0 +1,30 @@ +## +## maps/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +mapsdir = $(pkgdatadir)/maps + +maps_DATA =\ + maze.map\ + pattern.map\ + small.map + +EXTRA_DIST =\ + maze.map\ + pattern.map\ + small.map + diff --git a/maps/maze.map b/maps/maze.map new file mode 100644 index 0000000..3834a93 --- /dev/null +++ b/maps/maze.map @@ -0,0 +1,18 @@ +######################################## +# ##@@##$ #+++ ###########@@@$$# +# ##$@##$#@@ ###### $$$@@ #$$# +# ##$+##$#@# ### +#$$$# #####@### #### +# ##$+##$#$# +#$$$# #+++++#++# +# ##$+# $#$# ########$##### #$$$$$$$$# +# ##$+@@$#$##@@ @@@@$@@@@$#$$$###### +#@@@ @######@@ ###########$$#$$$###### +# @@ # #++ # ++#####++++++++++++# +######## #++ # +# +#########$$#++##+ $$$$$$$$$$$$$$$$$ +# +#+++$$$$$$ # #++ $$$$$$$$$$$$$$$$$ +# +#+++######## ##++ $ +++++++++++ $ +# +#+++######@ ##++ $ +++++++++++ $ +# +#####@ #@ ##++ $$$$$$$$$$$$$$$$$ +# +#++@@@$# #@#$$########$$$$$$$$$$$$$$ +# +#++ $$$#+++#$$$#+++++++++++++++++++++++# +######################################## diff --git a/maps/pattern.map b/maps/pattern.map new file mode 100644 index 0000000..ec1f29d --- /dev/null +++ b/maps/pattern.map @@ -0,0 +1,21 @@ +############################################## +#+ @ $#+ @ $#+ @ $#+ @ $#+ @ $# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$# +#+ @ $#+ @ $#+ @ $#+ @ $#+ @ $# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$#+# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$# +#+ @ $#+ @ $#+ @ $#+ @ $#+ @ $# +#+$#+ @ $#+ @ $#+ @ $#+ @ $#+ $# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$#+# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$#+# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$# +#+ @ $#+ @ $#+ @ $#+ @ $#+ @ $# +#+$#+ @ $#+ @ $#+ @ $#+ @ $#+ $# +##+ @ $#+ @ $#+ @ $#+ @ $#+$#+ $# +#+$#+ @ $#+ @ $#+ @ $#+ @ $#+ $# +#+ $#+ @ $#+ @ $#+ @ $#+ @ $#+$#+# +#+$#+ @ $#+ @ $#+ @ $#+ @ $#+$#+$# +#+ @ $#+ @ $#+ @ $#+ @ $#+ @ $# +#+$#+ @ $#+ @ $#+ @ $#+ @ $#+ $# +#+#+ @ $#+ @ $#+ @ $#+ @ $#+$#+ $# +############################################## diff --git a/maps/small.map b/maps/small.map new file mode 100644 index 0000000..91fbe04 --- /dev/null +++ b/maps/small.map @@ -0,0 +1,10 @@ +########################## +#$$$$$$$$$$$$$$$$$$$$$$$$# +#$ $# +#$ ### @@ $# +#$ @ ++ $# +#$ ++ $# +#$ @ ++ +@+ $# +#$ $# +#$$$$$$$$$$$$$$$$$$$$$$$$# +########################## diff --git a/scheme/Makefile.am b/scheme/Makefile.am new file mode 100644 index 0000000..1a379e9 --- /dev/null +++ b/scheme/Makefile.am @@ -0,0 +1,36 @@ +## +## scheme/Makefile.am +## +## 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 2 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, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## + +schemedir = $(pkgdatadir)/scheme + +scheme_SCRIPTS =\ + beep.scm\ + greedy.scm\ + mapper.scm\ + simple.scm\ + stop.scm\ + zap.scm + +EXTRA_DIST =\ + beep.scm\ + greedy.scm\ + mapper.scm\ + simple.scm\ + stop.scm\ + zap.scm + diff --git a/scheme/beep.scm b/scheme/beep.scm new file mode 100644 index 0000000..72b462b --- /dev/null +++ b/scheme/beep.scm @@ -0,0 +1,30 @@ +;;; beep.scm +;;; Sample robot provided by Jim Hall +;;; This robot will just turn around 360-degrees, and will beep if it finds +;;; a prize item. This is similar to a radar. + +;;; Define a function that will generate an audible beep + +(define (beep) (display "\a")) + +;;; Define a function that turns one unit, then feels for a prize. +;;; If we find a prize, make a beep. + +(define (turn-and-feel) + (robot-turn 1) + (if (robot-feel "prize") (beep)) +) + +;;; Make one sweep: + +(turn-and-feel) +(sleep 1) + +(turn-and-feel) +(sleep 1) + +(turn-and-feel) +(sleep 1) + +(turn-and-feel) +(sleep 1) diff --git a/scheme/greedy.scm b/scheme/greedy.scm new file mode 100644 index 0000000..79a57f8 --- /dev/null +++ b/scheme/greedy.scm @@ -0,0 +1,112 @@ +;;; +;;; Greedy robot for GNU Robots 0.77 +;;; 1998-08-15 by Kyle Hasselbacher +;;; + +;;; Greedy Robot wanders around looking for food and valuable prizes. +;;; The definitions for thing-one and thing-two determine which it thinks +;;; is more important (thing-one is). +;;; It's not always very efficient in its gathering and can easily walk in +;;; a circle if it can't see anything worth grabbing. It also assumes that +;;; there is nothing interesting behind it, and that's not always true. +;;; It treats baddies and walls the same way in that they are not +;;; food, prizes, or open space. It just avoids running into them. + +(define thing-one "food") +(define thing-two "prize") + +;;; If something interesting has been spotted, in-sights holds the name +;;; of the thing we're headed for. Once it's grabbed, we set! in-sights +;;; back to #f. Using the variable saves us a little energy since we don't +;;; have to keep looking at the same thing over and over. +(define in-sights #f) + +;;; grab a single prize (and move onto the space that had it) +(define (grab) + (if (robot-grab) + (robot-move 1)) + (set! in-sights #f)) + +;;; Look around! +(define (look-left-right thing) + (if (not (equal? in-sights thing)) + (robot-turn 1)) + (if (and (not (equal? in-sights thing)) (robot-look thing)) + (set! in-sights thing)) + (if (not (equal? in-sights thing)) + (robot-turn 2)) + (if (and (not (equal? in-sights thing)) (robot-look thing)) + (set! in-sights thing))) + +(define (seek-thing thing) + (if (and (not (equal? in-sights thing)) (robot-look thing)) + (set! in-sights thing)) + (look-left-right thing) + (if (not (equal? in-sights thing)) + (robot-turn 1)) + (equal? in-sights thing)) + +;;; Book it for a priority-one item. We don't look around for anything +;;; else, but we do sniff the air for items of the same type we might pass +;;; on the way. +(define (get-thing thing) + (if (robot-feel thing) + (grab) + (begin (smell-test thing) + (if (robot-move 1) + (get-thing thing))))) + +;;; This doesn't do any smelling. It's called by smell-test when it thinks +;;; there might be something to the left (it's already checked to the right). +(define (smell-behind thing) + (robot-turn 2) + (if (robot-feel thing) + (robot-grab)) + (robot-turn 1)) + +;;; This feels to the right and left if it smells the thing its asked about. +;;; Do the smell test after (robot-feel thing) fails. +;;; Otherwise you spin around from smelling what's in front of you. +;;; smell-test does a lot of turning but leaves you facing the same way. +(define (smell-test thing) + (if (robot-smell thing) + (begin + (robot-turn 1) + (if (robot-feel thing) + (begin + (robot-grab) + (if (robot-smell thing) + (smell-behind thing) + (robot-turn -1))) + (smell-behind thing))))) + +;;; The drunkard's walk isn't too drunk. +;;; Without obstructions, it only turns once in a while (1/10 steps). +(define (drunkard-walk) + (if (and (not (= (random 10) 0)) (robot-feel "space")) + (robot-move 1) + (begin (robot-turn (+ 1 (random 2))) (drunkard-walk)))) + +;;; The main loop! We go for our thing-one if we can see it. If we can see +;;; thing-two, we go for that while checking for thing-one. If we can see +;;; neither, we drunkards-walk one step. +(define (main-loop) + (cond ((seek-thing thing-one) + (get-thing thing-one) + (set! in-sights #f)) + + ((seek-thing thing-two) + (if (robot-feel thing-two) + (grab) + (begin + (smell-test thing-two) + (robot-move 1)))) + + (#t (drunkard-walk))) + (main-loop)) + +;;; Be random +;(randomize) + +;;; Go to it. +(main-loop) diff --git a/scheme/mapper.scm b/scheme/mapper.scm new file mode 100644 index 0000000..ba00c76 --- /dev/null +++ b/scheme/mapper.scm @@ -0,0 +1,827 @@ +; +; Mapping robot for GNU Robots 0.77 +; 1998-08-22 by Kyle Hasselbacher +; + +; The central idea here is that the robot keeps a map of where it's been. +; +; BEHAVIOR +; The robot will move in a straight line until it encounters a wall or some +; place that it's already been. When it gets there, it will head for some +; place it hasn't been. +; Any time the robot is in a place it hasn't been before, it will feel the +; spaces around it as necessary to find out what's there. If it feels +; something it can grab, it does. +; +; PROBLEMS +; (1) Speed. If the robot is far away from a "frontier", it takes a long +; time for it to find a path there. +; (2) It sometimes does some unnecessary turning. The (feel-around) +; function always leaves it facing as it was, but after that it'll want to +; turn anway because there's something in front of it. +; (3) Its exploration isn't particularly systematic. It never really makes +; zero progress, but it sometimes goes goes over ground multiple times when +; it doesn't need to. +; +; NOTES +; It keeps a list of frequencies of everything it's found so that it always +; feels for the most prevalent map items first. +; The map data structure stretches as the robot expands its area of +; knowledge. +; The robot also keeps track of its location and orientation. + +;;; +;;; Variables +;;; +(define freq '(("space" 1) ("wall" 0) ("baddie" 0) ("food" 0) ("prize" 0))) + +; Oops. I redefined a primitive. Well, I didn't like that function anyway... +(define map '()) + +; Assumed to start facing east at the origin. +(define facing (list 'east 'south 'west 'north)) +(define loc (cons 1 1)) + +(define map-wide 0) +(define map-tall 0) + +; We change this over the life of the robot, to keep it from getting hung up +;(define favorite-direction 'east) +;(define on-frontier #t) + +; If predict-death is true, the robot will dump its map to the screen and +; exit when it think it's low on energy. +(define predict-death #t) +(define energy 1000) ; Starting energy + +; If this is true, the robot will cut short path searches as soon as it +; finds a place it hasn't been before. This sometimes makes the robot +; unnecessarily aggressive because it will see a path THROUGH a baddie +; to a frontier before it notices the (cheaper) path around it. +(define loose-goals #f) + +;;; +;;; Mapping functions +;;; +(define (init-map x y) + (set! map-wide x) + (set! map-tall y) + (map-rows (+ 2 x) (+ 2 y))) + +(define (widen-map n) + (set! map-wide (+ n map-wide)) + (set! map (widen-map-n map n))) + +(define (widen-map-n map n) + (if (null? map) + '() + (cons (append (car map) (make-list n #f)) (widen-map-n (cdr map) n)))) + +(define (heighten-map n) + (set! map-tall (+ n map-tall)) + (set! map (append map (map-rows (list-count (car map)) n)))) + +; Yuck! This doesn't work: (make-list y (make-list x #f)) +; because every row is a pointer to the same list! +(define (map-rows x n) + (if (< n 1) + '() + (cons (make-list x #f) (map-rows x (- n 1))))) + +(define (mark-map! loc thing) + (list-set! (list-ref map (cdr loc)) (car loc) thing)) + +;;; +;;; Path finding +;;; + +; +; We have a map, so we should be able to find a path from one part of the +; map to another without having to grope around, right? Here's how: +; +; (1) Make a list of possible paths. Each path is a list containing the +; "cost" of taking that path (including a heuristic estimate) and the +; points along the path. +; (2) Extend the current least-cost path in every possible direction +; (creating new paths) and generate new cost estimates. +; (3) Eliminate paths to duplicate locations (keeping the least-cost path). +; (4) Eliminate paths with loops. +; (5) Sort the list of paths. +; +; The heuristic will probably be the horizontal difference plus the +; vertical difference plus one. Note that it costs to turn, so we need to +; keep track of how the robot is facing too. We'll make it cost 5 to move +; through a baddie since you can do it if you zap 'im. +; + +; +; A lot of the code below was stolen wholesale (with comments) directly +; from my second assignment in CS 348 (Intro to AI) at the U of I four +; years ago. It was written in LISP, so few changes were necessary, but +; variable names aren't always consistent. +; + +; PATH DATA STRUCTURE: +; It's a list that looks like this: (123 'north (1 . 2) (3 . 4)) +; The number is the estimated cost of the path from beginning to +; destination (NOT from beginning to the current end of the path) +; The direction is the initial orientation of the robot. It never +; changes. +; The first pair is the location the path started. The second is the first +; step of the path, etc. The last pair in the list is the end of the path +; right now (which may not be at the goal). + +; This will return a path including its cost. +(define (find-path dest-loc) + (find-path-a dest-loc (list (list (guess-cost loc dest-loc) + (car facing) loc)))) + +(define (find-path-a dest-loc path-list) + (d-list (list "find-path " dest-loc " " path-list "\n")) + (cond ((null? path-list) path-list) + ((is-goal? dest-loc (car (last-pair (car path-list)))) (car path-list)) + (#t (find-path-a dest-loc + (sort-paths + (elim-common-dest + (reject-loops + (append (cdr path-list) + (new-paths (car path-list) + dest-loc))))))))) + +; The last location in the path. +(define (end-path path) + (list-ref path (- (list-count path) 1))) + +; How much it costs to take a particular set of steps. +; It needs to know the final destination so it can guess the cost of the +; rest of the steps to get there. +; It needs to know the initial orientation so that it can detect turns. +(define (steps-cost steps dest face) +; (d-list (list "steps-cost " steps " " dest " " face "\n")) + (if (null? (cdr steps)) + (guess-cost (car steps) dest) + (+ (if (equal? face (tell-direction (car steps) (cadr steps))) + 0 1) + (cond ((equal? (at-loc (car steps)) "space") 1) + ((equal? (at-loc (car steps)) "baddie") 6) + (#t 10000)) + (steps-cost (cdr steps) dest (tell-direction (car steps) + (cadr steps)))))) + +(define (guess-cost start-loc dest-loc) + (+ (abs (- (car start-loc) (car dest-loc))) + (abs (- (cdr start-loc) (cdr dest-loc))) + 1)) + +; Find more paths based on this path. +(define (new-paths path dest-loc) + (recompute-costs dest-loc + (path-sanity (list + (append path (list (vector-loc 'north (end-path path)))) + (append path (list (vector-loc 'south (end-path path)))) + (append path (list (vector-loc 'east (end-path path)))) + (append path (list (vector-loc 'west (end-path path)))))))) + +; Throw out paths that try to go through anything other than spaces or +; baddies. +; Throw out paths that go through points outside the map. +(define (path-sanity path-list) + (if (null? path-list) + '() + (if (or (and (not (equal? (at-loc (end-path (car path-list))) "space")) + (not (equal? (at-loc (end-path (car path-list))) "baddie"))) + (out-of-bounds? (end-path (car path-list)))) + (path-sanity (cdr path-list)) + (cons (car path-list) (path-sanity (cdr path-list)))))) + +(define (recompute-costs dest-loc path-list) +; (d-list (list "recompute-costs " dest-loc " " path-list "\n")) + (if (null? path-list) + '() + (begin + (set-car! (car path-list) (steps-cost (cddar path-list) dest-loc + (cadar path-list))) + (cons (car path-list) (recompute-costs dest-loc (cdr path-list)))))) + +; +; Takes a path list and removes those paths which contain loops (double +; occurrances of any one node). Each path is checked with the looping +; procedure. +; +(define (reject-loops path-list) +; (d-list (list "reject-loops " path-list "\n")) + (if (null? path-list) + path-list + (if (looping (car path-list)) + (reject-loops (cdr path-list)) + (cons (car path-list) (reject-loops (cdr path-list)))))) +; +; A path is checked for loops by checking each node for membership in the +; remainder of the path. +; +(define (looping path) +; (d-list (list "looping " path "\n")) + (if (null? path) + #f + (if (pair? (car path)) + (or (member (car path) (cdr path)) + (looping (cdr path))) + (looping (cdr path))))) + +; This was a LISP primitive. It might be a Scheme primitive too, but it +; didn't do what I wanted. (The original code used symbols for nodes, but +; this is using pairs.) +(define (member test-loc loc-list) + (if (null? loc-list) + #f + (or (loc-eq? test-loc (car loc-list)) + (member test-loc (cdr loc-list))))) + +; +; This takes a list of paths and eliminates those which end at the same +; node. It takes a list of all paths which have the same ending as the +; current path (provided by same-end)--this list can contain only one +; thing--and takes the shortest of those paths to remain in the list. +; +(define (elim-common-dest path-list) + (if (null? path-list) + '() + (cons (shortest-path (same-end (end-path (car path-list)) + path-list)) + (elim-common-dest (elim-dest (end-path (car path-list)) + (cdr path-list)))))) + +; +; This takes a path list and an ending node, and returns all paths in the +; list which do not end at that node. It's used to eliminate paths which +; end at the same node. +; +(define (elim-dest dest path-list) + (if (null? path-list) + '() + (if (loc-eq? dest (end-path (car path-list))) + (elim-dest dest (cdr path-list)) + (cons (car path-list) + (elim-dest dest (cdr path-list)))))) + +; +; This takes an ending node and a path list and returns all paths in the +; list which DO end at that node. It's used to FIND paths which end at the +; same node. +; +(define (same-end end path-list) + (if (null? path-list) path-list + (if (loc-eq? end (end-path (car path-list))) + (cons (car path-list) + (same-end end (cdr path-list))) + (same-end end (cdr path-list))))) +; +; This is just a proper call to real-sp, which does the real work of +; finding the shortest path in a list of paths. It returns a path. +; +(define (shortest-path path-list) + (real-sp '() path-list)) +; +; This recursive function finds the shortest path in a list. The first +; argument is the shortest path found so far, and the second argument is +; the list of paths for comparison. +; +(define (real-sp shortest path-list) + (if (null? path-list) shortest + (if (and (number? (caar path-list)) ; Just in case path-list is messed. + (or (null? shortest) + (< (caar path-list) (car shortest)))) + (real-sp (car path-list) (cdr path-list)) + (real-sp shortest (cdr path-list))))) + +; Tell whether our current endpoint is the goal. If loose-goals is true, +; this will include any location we haven't already visited. This might +; later be expanded to include "good enough" goals for when the real goal +; is completely inaccessible. +(define (is-goal? dest-loc cur-loc) + (or (loc-eq? dest-loc cur-loc) + (and loose-goals (not (been-to cur-loc))))) + +; Test whether two locations are equal. +(define (loc-eq? a b) + (and (= (car a) (car b)) (= (cdr a) (cdr b)))) + +(define (sort-paths path-list) + (quicksort path-list (lambda (a b) + (cond ((< (car a) (car b)) 'less-than) + ((= (car a) (car b)) 'equal-to) + ((> (car a) (car b)) 'greater-than))))) + +(define (execute-path path) + (d-list (list "execute-path " path "\n")) + (execute-steps (cdddr path))) + +; Go through the steps dictated by a path. This will make all the turns, +; moves, and zaps necessary to get you where you're going according to the +; plan. +(define (execute-steps step-list) + (d-list (list "execute-steps " step-list "\n")) + (if (null? step-list) + '() + (begin + (turn-face (tell-direction loc (car step-list))) + (if (equal? (at-loc (car step-list)) "baddie") + (zap)) + (move 1) + (execute-steps (cdr step-list))))) + +;;; +;;; Action wrappers +;;; + +(define (zap) + (decr-energy 5) + (if (robot-zap) + (mark-map! (front-loc) "space"))) + +; Maybe this should also note the spaciness of intervening map squares, +; but hopefully we won't move into them if they're not spaces. +(define (move n) + (decr-energy n) + (if (robot-move n) + (begin + (change-loc n) + (if (< map-wide (car loc)) + (widen-map (- (car loc) map-wide))) + (if (< map-tall (cdr loc)) + (heighten-map (- (cdr loc) map-tall))) + (feel-around)) + #f)) + +(define (change-loc n) + (if (= n 0) + '() + (begin + (set! loc (front-loc)) + (change-loc (- n 1))))) + +(define (turn n) + (decr-energy (abs n)) + (change-face n) + (robot-turn n)) + +(define (change-face n) + (if (= n 0) + '() + (begin + (if (> n 0) + (begin + (set! facing (append (cdr facing) (list (car facing)))) + (change-face (- n 1)))) + (if (< n 0) + (begin + (set! facing (list (list-ref facing 3) (list-ref facing 0) + (list-ref facing 1) (list-ref facing 2))) + (change-face (+ n 1))))))) + +;;; +;;; Sensory functions. +;;; + +; This will feel in front of the robot for everything it knows and grab +; things that are worth grabbing. +(define (grope) + (let ((thing (grope-things freq))) + (note-freq! freq thing) + (if (or (equal? thing "food") + (equal? thing "prize")) + (begin + (robot-grab) + (if (equal? thing "food") + (set! energy (+ 10 energy))) + (decr-energy 1) + "space") + thing))) + +; This does the actual feeling for the individual things in the frequency +; list. +(define (grope-things freq) + (if (null? freq) + #f + (begin (decr-energy 1) + (if (robot-feel (caar freq)) + (caar freq) + (grope-things (cdr freq)))))) + +; This makes sure the robot knows its immediate surroundings. It's called +; after every movement. It won't feel spaces it's already felt, and it +; always leaves the robot facing the same direction it started. +(define (feel-around) + (let ((start-face (car facing))) + (feel-directions facing) + (turn-face start-face))) + +(define (feel-directions face-list) + (if (null? face-list) + '() + (begin + (if (not (at-loc (vector-loc (car face-list) loc))) + (begin +; (set! on-frontier #t) + (turn-face (car face-list)) + (mark-map! (front-loc) (grope)))) + (feel-directions (cdr face-list))))) + +; An old version of feel-around. + +;(define (feel-around) +; (if (not (at-loc (front-loc))) +; (mark-map! (front-loc) (grope))) +; (if (not (at-loc (right-loc))) +; (begin +; (turn 1) ; right from "front" +; (mark-map! (front-loc) (grope)) +; (if (not (at-loc (back-loc))) +; (begin +; (turn 2) ; left from "front" +; (mark-map! (front-loc) (grope)) +; (turn 1)) ; front +; (turn -1))) ; front +; (if (not (at-loc (left-loc))) +; (begin +; (turn -1) ; left from "front" +; (mark-map! (front-loc) (grope)) +; (turn 1))))) ; front + +; This changes the frequency list to reflect the last thing we felt. +(define (note-freq! freq thing) + (if (null? freq) + '() + (if (equal? (caar freq) thing) + (set-car! freq (list thing (+ 1 (cadar freq)))) + (note-freq! (cdr freq) thing)))) + +(define (sort-freq!) + (set! freq (quicksort freq (lambda (a b) + (cond ((> (cadr a) (cadr b)) 'less-than) + ((= (cadr a) (cadr b)) 'equal-to) + ((< (cadr a) (cadr b)) 'greater-than)))))) + +; THE SCHEMATICS OF COMPUTATION by Vincent S. Manis and James J. Little +; page 487 +(define quicksort + (lambda (x compare) + (if (null? x) + x + (let* + ((pivot (car x)) + (smaller '()) (equal '()) (larger '()) + (classify + (lambda (item) + (case (compare item pivot) + ((less-than) + (set! smaller (cons item smaller))) + ((equal-to) + (set! equal (cons item equal))) + ((greater-than) + (set! larger (cons item larger))))))) + (for-each classify x) +; (format #t "smaller: ~a equal: ~a larger: ~%" +; smaller equal larger) + (append (quicksort smaller compare) + equal (quicksort larger compare)))))) + +;;; +;;; Orientation-related functions +;;; + +; These give the coordinates of spots around the robot +(define (front-loc) + (relative-loc 'front loc)) + +(define (back-loc) + (relative-loc 'back loc)) + +(define (right-loc) + (relative-loc 'right loc)) + +(define (left-loc) + (relative-loc 'left loc)) + +; Tell me a direction (right, left, front, back) and a location, and I'll +; tell you the coordinate in the direction from the location. This uses +; the current orientation of the robot to do its computation. +(define (relative-loc dir loc) + (case dir + ((left) (case (car facing) + ((west) (cons (car loc) (+ 1 (cdr loc)))) + ((east) (cons (car loc) (- (cdr loc) 1))) + ((north) (cons (- (car loc) 1) (cdr loc))) + ((south) (cons (+ (car loc) 1) (cdr loc))))) + ((right) (case (car facing) + ((east) (cons (car loc) (+ 1 (cdr loc)))) + ((west) (cons (car loc) (- (cdr loc) 1))) + ((south) (cons (- (car loc) 1) (cdr loc))) + ((north) (cons (+ (car loc) 1) (cdr loc))))) + ((back) (case (car facing) + ((south) (cons (car loc) (- (cdr loc) 1))) + ((north) (cons (car loc) (+ 1 (cdr loc)))) + ((west) (cons (+ (car loc) 1) (cdr loc))) + ((east) (cons (- (car loc) 1) (cdr loc))))) + ((front) (case (car facing) + ((south) (cons (car loc) (+ 1 (cdr loc)))) + ((north) (cons (car loc) (- (cdr loc) 1))) + ((west) (cons (- (car loc) 1) (cdr loc))) + ((east) (cons (+ (car loc) 1) (cdr loc))))))) + +; Tell me a vector (north, south, east, west) and a location, and I'll tell +; you the location in that direction from your location. +(define (vector-loc face loc) + (case face + ((east) (cons (+ (car loc) 1) (cdr loc))) + ((west) (cons (- (car loc) 1) (cdr loc))) + ((north) (cons (car loc) (- (cdr loc) 1))) + ((south) (cons (car loc) (+ (cdr loc) 1))))) + +; Turn in a particular direction (north, south, east, west). +(define (turn-face face) + (case face + ((east) (case (car facing) + ((east) #t) + ((west) (turn 2)) + ((north) (turn 1)) + ((south) (turn -1)))) + ((west) (case (car facing) + ((west) #t) + ((east) (turn 2)) + ((north) (turn -1)) + ((south) (turn 1)))) + ((north) (case (car facing) + ((north) #t) + ((south) (turn 2)) + ((east) (turn -1)) + ((west) (turn 1)))) + ((south) (case (car facing) + ((south) #t) + ((north) (turn 2)) + ((east) (turn 1)) + ((west) (turn -1)))))) + +;;; +;;; Unorganized functions. +;;; + +(define (decr-energy n) + (set! energy (- energy n)) + (d-list (list "*** energy " energy " ***\n")) + (if (and predict-death (< energy 11)) + (dump))) + +; I bet there's a primitive to do this, but I can write this faster +; than I can look it up. +(define (list-count tsil) + (if (null? tsil) + 0 + (+ 1 (list-count (cdr tsil))))) + +; Tell whether a coordinate is outside the map. +(define (out-of-bounds? loc) + (or (> 1 (car loc)) (> 1 (cdr loc)) + (> (car loc) (- (list-count (car map)) 1)) + (> (cdr loc) (- (list-count map) 1)))) + +; Tell what's at a particular location. This will say "wall" for +; out-of-bounds locations to the north or west and #f for out-of-bounds +; locations to the south or east (since the map may be stretched in that +; direction, theoretically). +(define (at-loc loc) + (if (or (> 0 (car loc)) (> 0 (cdr loc))) + "wall" + (if (or (> (car loc) (- (list-count (car map)) 1)) + (> (cdr loc) (- (list-count map) 1))) + #f + (list-ref (list-ref map (cdr loc)) (car loc))))) + +; Tell whether we've visited a particular location. It checks whether we +; know what's at that location and the locations around it, so you can get +; a true return even if you haven't actually stepped on the spot, but it +; still means you don't need to go there. +(define (been-to loc) + (and (at-loc loc) + (at-loc (cons (+ 1 (car loc)) (cdr loc))) + (at-loc (cons (- (car loc) 1) (cdr loc))) + (at-loc (cons (car loc) (- (cdr loc) 1))) + (at-loc (cons (car loc) (+ 1 (cdr loc)))))) + +; This gives a list of coordinates that surround a particular location at a +; particular "radius." The coordinates actually form a square. If the +; radius is 1, you'll get 8 coordinates. If the radius is 2, you get 16. +; This is used to search for locations of a particular type in a radiating +; fashion from the robot itself. +(define (coord-around-list loc radius) + (append (h-coord-list (cons (- (car loc) radius) (- (cdr loc) radius)) + (+ 1 (* 2 radius))) + (v-coord-list (cons (+ (car loc) radius) (+ 1 (- (cdr loc) radius))) + (- (* 2 radius) 1)) + (h-coord-list (cons (- (car loc) radius) (+ (cdr loc) radius)) + (+ 1 (* 2 radius))) + (v-coord-list (cons (- (car loc) radius) (+ 1 (- (cdr loc) radius))) + (- (* 2 radius) 1)))) + +(define (h-coord-list loc n) + (if (= n 0) + '() + (cons loc (h-coord-list (cons (+ 1 (car loc)) (cdr loc)) (- n 1))))) + +(define (v-coord-list loc n) + (if (= n 0) + '() + (cons loc (v-coord-list (cons (car loc) (+ 1 (cdr loc))) (- n 1))))) + +; Gimme a function and a list. I'll give you a list of pairs. +; (function-results . pair) Of course, the function should take one argument. + +;(define (coord-list-apply func loc-list) +; (if (null? loc-list) +; '() +; (cons (cons (func (car loc-list)) +; (car loc-list)) +; (coord-list-apply func (cdr loc-list))))) + +;(define (find-result x tsil) +; (if (null? tsil) +; '() +; (if (eqv? x (caar tsil)) +; (cons (cdar tsil) +; (find-result x (cdr tsil))) +; (find-result x (cdr tsil))))) + + +; What direction is loc2 from loc1 ? +; (If the direction is an exact diagonal, I don't know what you'll get, but +; I don't think that's particularly wrong either.) +(define (tell-direction loc1 loc2) + (if (> (abs (- (car loc1) (car loc2))) + (abs (- (cdr loc1) (cdr loc2)))) + (if (< 0 (- (car loc1) (car loc2))) + 'west + 'east) + (if (< 0 (- (cdr loc1) (cdr loc2))) + 'north + 'south))) + +;(define (new-favorite-direction) +;; (d-list (list "--- favorite direction is: " favorite-direction "\n")) +; (set! on-frontier #f) +; (let* ((cur-dir favorite-direction) +; (face-dir (car facing)) +; (new-dir (tell-direction loc (find-frontier 1)))) +; (if (equal? new-dir cur-dir) +; (if (equal? new-dir face-dir) +; (set! favorite-direction (cadr facing)) +; (set! favorite-direction face-dir)) +; (set! favorite-direction new-dir)))) + +; Call this with an initial argument of 1. It will search outward from the +; robot for a location it hasn't yet visited. It will return a list of +; such locations which are all roughly the same distance from the robot. +; This is used to select a new destination for the robot when it's gotten +; stuck somewhere. +(define (find-frontier n) + (d-list (list "--- find-frontier " n " ---\n")) + (if (> n 20) (dump)) ; PROBABLY a problem + (let ((result (frontier-do n))) + (d-list (list result "\n")) + (if (and (not (null? result)) + (equal? "baddie" (at-loc (car result))) + (= n 1)) + (set! result (append (frontier-do 2) result))) + (if (null? result) + (find-frontier (+ n 1)) + (car result)))) + +; Produces a list of possible frontier values (sorted and sanity checked). +(define (frontier-do n) + (quicksort ; Spaces are better than baddies. + (frontier-sanity (coord-around-list loc n)) + (lambda (a b) + (cond ((and (equal? (at-loc a) "space") + (equal? (at-loc b) "baddie")) 'less-than) + ((and (equal? (at-loc a) "baddie") + (equal? (at-loc b) "space")) 'greater-than) + (#t 'equal-to))))) + +; This keeps the results of find-frontier in check. We throw out: +; +; (1) Locations that are off the map. +; (2) Walls. +; (3) Locations we haven't mapped (find-path doesn't know how to get there). +; (4) Locations we've already visited. +; +; That way find-frontier should give us a spot which is exatly next to a +; spot we haven't mapped. +(define (frontier-sanity loc-list) + (if (null? loc-list) + '() + (if (or (out-of-bounds? (car loc-list)) + (equal? (at-loc (car loc-list)) "wall") + (not (at-loc (car loc-list))) + (been-to (car loc-list))) + (frontier-sanity (cdr loc-list)) + (cons (car loc-list) (frontier-sanity (cdr loc-list)))))) + +; This decides how we move. +(define (go) + (if (and (equal? (at-loc (front-loc)) "space") + (not (been-to (front-loc)))) + (move 1) + (let ((path (find-path (find-frontier 1)))) + (if (null? path) + (dump) + (execute-path path))))) + +;(define (go) +; (if (and (equal? (at-loc (vector-loc favorite-direction loc)) "space") +; (or (not on-frontier) +; (not (been-to (vector-loc favorite-direction loc)))) +; (not (equal? (car facing) favorite-direction))) +; (turn-face favorite-direction) +; (if (equal? (at-loc (front-loc)) "space") +; (move 1) +; (if (and (equal? (at-loc (right-loc)) "space") +; (or (not on-frontier) (not (been-to (right-loc))))) +; (turn 1) +; (if (and (equal? (at-loc (left-loc)) "space") +; (or (not on-frontier) (not (been-to (left-loc))))) +; (turn -1) +; (let ((path (find-path (find-frontier 1)))) +; (if (null? path) +; (dump) +; (execute-path path)))))))) + + +; (new-favorite-direction) +; (turn-face favorite-direction))))))) + +;;; +;;; Debugging functions. +;;; + +; Dummies (so the guile interpreter doesn't blow up) +;(define (robot-feel n) (display "robot-feel\n") "space") +;(define (robot-move n) (display "robot-move\n") #t) +;(define (robot-turn n) (display "robot-turn\n") #t) +;(define (robot-grab) (display "robot-grab\n") #t) + +;(define (d-list tsil) (if (null? tsil) '() (cons (display (car tsil)) (d-list (cdr tsil))))) + +(define (d-list tsil) tsil) + +; Print the map to the screen and exit. +(define (dump) + (display-map map) + (quit)) + +(define (display-map map) + (if (null? map) + '() + (begin + (display-map-line (car map)) + (display-map (cdr map))))) + +(define (space-out n) + (if (= n 0) + '() + (begin + (display " ") + (space-out (- n 1))))) + +(define (display-map-line tsil) + (if (null? tsil) + (space-out (- 80 (list-count (car map)))) +; (display "\n") + (begin + (if (equal? "space" (car tsil)) + (display ".")) + (if (equal? "baddie" (car tsil)) + (display "@")) + (if (equal? "wall" (car tsil)) + (display "#")) + (if (not (car tsil)) + (display "x")) + (if (equal? "food" (car tsil)) + (display "+")) + (if (equal? "prize" (car tsil)) + (display "$")) + (display-map-line (cdr tsil))))) + +;;; +;;; Main program. +;;; + +(define (main-loop) + (go) + (main-loop)) + +; INITIALIZATION. +(set! map (init-map 1 1)) ; The map is tiny, but it will grow. +(sort-freq!) ; Sort the frequency list if it isn't already. +(mark-map! loc "space") ; I start out on a space, Shirly. +(feel-around) ; Get your bearings. + +(main-loop) ; GO. diff --git a/scheme/simple.scm b/scheme/simple.scm new file mode 100644 index 0000000..66f3591 --- /dev/null +++ b/scheme/simple.scm @@ -0,0 +1,32 @@ +;;; simple.scm +;;; Sample robot provided by Jim Hall +;;; This robot will simply hunt down and grab any prizes in its direct +;;; line of sight. If it runs into an obstacle, it turns right and +;;; continues from there. When it has turned 360-degrees, it stops. + +;;; Define a function to feel for prize (wrapper) +(define (feel-prize) + (robot-feel "prize")) + +;;; Define a function to grab a single prize +(define (grab-prize) + (robot-grab) + (robot-move 1)) + +;;; Define a function to grab all prizes +(define (grab-all-prizes) + (do () (not (feel-prize)) (grab-prize))) + +;;; The program starts here: hunt for all prizes + +(grab-all-prizes) +(robot-turn 1) + +(grab-all-prizes) +(robot-turn 1) + +(grab-all-prizes) +(robot-turn 1) + +(grab-all-prizes) +(sleep 1) diff --git a/scheme/stop.scm b/scheme/stop.scm new file mode 100644 index 0000000..511988b --- /dev/null +++ b/scheme/stop.scm @@ -0,0 +1,16 @@ +;;; stop.scm +;;; Sample robot provided by Jim Hall +;;; THIS ROBOT IS NOT REALLY INTENDED FOR PUBLIC CONSUMPTION! +;;; Tests my new `stop' and `quit' primitives for GNU Robots + +;;; Define a function to make a beep +(define (beep) (display "\a")) + +;;; The program starts here: + +(beep) +(sleep 1) + +;;; Test my new `stop' and `quit' primitives: +;(stop) +(quit) diff --git a/scheme/zap.scm b/scheme/zap.scm new file mode 100644 index 0000000..67e8d4d --- /dev/null +++ b/scheme/zap.scm @@ -0,0 +1,36 @@ +;;; zap.scm +;;; Sample robot provided by Jim Hall +;;; This is an agressive little robot that will just turn 360-degrees, +;;; and will immediately zap anything that isn't a space. This builds +;;; on the beep.scm robot program, so it will also beep if it finds a +;;; prize (but then destroys it.) + +;;; Define a function to make a beep +(define (beep) (display "\a")) + +;;; Define a function to blow away anything that isn't a space +(define (blast-nonspace) + (if (robot-feel "space") (robot-zap))) + +;;; Define a function to turn, then see if a prize is there +(define (turn-and-feel) + (robot-turn 1) + (if (robot-feel "prize") (beep))) + +;;; The program begins here: make one sweep + +(turn-and-feel) +(blast-nonspace) +(sleep 1) + +(turn-and-feel) +(blast-nonspace) +(sleep 1) + +(turn-and-feel) +(blast-nonspace) +(sleep 1) + +(turn-and-feel) +(blast-nonspace) +(sleep 1) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..47d7f3b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,12 @@ +bin_PROGRAMS = grobots + +INCLUDES = $(GLIB2_CFLAGS) $(GUILE_CFLAGS) -I$(top_builddir)/include \ + -DPKGLIBDIR=\"$(pkglibdir)\" \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -DMAPS_PATH=\"$(mapsdir)\" \ + -DSCRIPTS_PATH=\"$(schemedir)\" + +grobots_SOURCES = main.c api.c sign.c map.c grobot.c userinterface.c +grobots_LDFLAGS = $(GLIB2_LIBS) $(GUILE_LDFLAGS) -lltdl + diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000..81776e5 --- /dev/null +++ b/src/api.c @@ -0,0 +1,160 @@ +/* $Id: api.c,v 1.16 2005/09/06 19:55:40 zeenix Exp $ */ + +/* Robot API for the GNU Robots game */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include /* for abs, free */ + +#include +#include /* GNU Guile high */ + +#include "api.h" /* GNU Robots API */ +#include "main.h" /* for exit_nicely */ +#include "sign.h" /* a hack for +/- sign */ +#include "grobot.h" + +extern GRobot *robot; + +/* Functions */ + +SCM +api_robot_turn (SCM s_n) +{ + g_robot_turn (robot, scm_num2int (s_n, 0, NULL)); + + return SCM_BOOL (TRUE); +} + +SCM +api_robot_move (SCM s_n) +{ + return SCM_BOOL (g_robot_move (robot, scm_num2int (s_n, 0, NULL))); +} + +SCM +api_robot_smell (SCM s_th) +{ + gboolean ret; + gchar *str; + + str = SCM_STRING_CHARS (s_th); + ret = g_robot_smell (robot, str); + + return SCM_BOOL (ret); +} + +SCM +api_robot_feel (SCM s_th) +{ + gboolean ret; + gchar *str; + + str = SCM_STRING_CHARS (s_th); + ret = g_robot_feel (robot, str); + + return SCM_BOOL (ret); +} + +SCM +api_robot_look (SCM s_th) +{ + gboolean ret; + gchar *str; + + str = SCM_STRING_CHARS (s_th); + ret = g_robot_look (robot, str); + + return SCM_BOOL (ret); +} + +SCM +api_robot_grab (void) +{ + return SCM_BOOL (g_robot_grab (robot)); +} + +SCM +api_robot_zap (void) +{ + return SCM_BOOL (g_robot_zap (robot)); +} + +SCM +api_robot_stop (void) +{ + return SCM_BOOL (g_robot_stop (robot)); +} + +SCM +api_robot_get_shields (void) +{ + glong shields; + + g_object_get (robot, "shields", &shields, NULL); + + /* Returns the robot shields */ + return (scm_long2num (shields)); +} + +SCM +api_robot_get_energy (void) +{ + glong energy; + + g_object_get (robot, "energy", &energy, NULL); + + /* Returns the robot energy */ + return (scm_long2num (energy)); +} + +SCM +api_robot_get_score (void) +{ + glong score; + + g_object_get (robot, "score", &score, NULL); + + /* Returns the robot score */ + return (scm_long2num (score)); +} + +void +api_init (void) +{ + /* define some new builtins (hooks) so that they are available in + Scheme. */ + + scm_c_define_gsubr ("robot-turn", 1, 0, 0, api_robot_turn); + scm_c_define_gsubr ("robot-move", 1, 0, 0, api_robot_move); + scm_c_define_gsubr ("robot-smell", 1, 0, 0, api_robot_smell); + scm_c_define_gsubr ("robot-feel", 1, 0, 0, api_robot_feel); + scm_c_define_gsubr ("robot-look", 1, 0, 0, api_robot_look); + scm_c_define_gsubr ("robot-grab", 0, 0, 0, api_robot_grab); + scm_c_define_gsubr ("robot-zap", 0, 0, 0, api_robot_zap); + + scm_c_define_gsubr ("robot-get-shields", 0, 0, 0, api_robot_get_shields); + scm_c_define_gsubr ("robot-get-energy", 0, 0, 0, api_robot_get_energy); + scm_c_define_gsubr ("robot-get-score", 0, 0, 0, api_robot_get_score); + + scm_c_define_gsubr ("stop", 0, 0, 0, api_robot_stop); + scm_c_define_gsubr ("quit", 0, 0, 0, api_robot_stop); +} + diff --git a/src/grobot.c b/src/grobot.c new file mode 100644 index 0000000..89602a0 --- /dev/null +++ b/src/grobot.c @@ -0,0 +1,833 @@ +/* Robot object for the GNU Robots game */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "grobot.h" +#include "configs.h" +#include "userinterface.h" /* GNU Robots UI */ +#include +#include + +enum +{ + DEATH, + LAST_SIGNAL +}; + +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 +}; + +static gchar *things[] = { "space", "food", "prize", "wall", "baddie", "robot" }; +static gint cthings[] = { SPACE, FOOD, PRIZE, WALL, BADDIE, ROBOT }; + +GType _g_robot_type; + +static guint g_robot_signals[LAST_SIGNAL] = { 0 }; +static void g_robot_class_init (GRobotClass * klass); + +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); + +static void g_robot_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GObjectClass *parent_class = NULL; + +static gint what_thing (const gchar *th); + +GType +g_robot_get_type (void) +{ + if (!_g_robot_type) { + static const GTypeInfo object_info = { + sizeof (GRobotClass), + NULL, + NULL, + (GClassInitFunc) g_robot_class_init, + NULL, + NULL, + sizeof (GRobot), + 0, + (GInstanceInitFunc) NULL, + NULL + }; + + _g_robot_type = + g_type_register_static (G_TYPE_OBJECT, + "GRobot", + &object_info, + 0); + } + + return _g_robot_type; +} + +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) { + 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); + } + + else { + robot->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 * +g_robot_new (gint x, gint y, gint dir, glong score, glong energy, glong shields, + glong units, glong shots, UserInterface *ui, Map *map) +{ + + return 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); +} + +static void +g_robot_dispose (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); +} + +static void +g_robot_finalize (GObject * object) +{ + parent_class->finalize (object); +} + +void +g_robot_turn (GRobot *robot, gint num_turns) +{ + 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 */ + user_interface_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 */ +} + +gboolean +g_robot_move (GRobot *robot, gint steps) +{ + gint x_to, y_to; + gint dx, dy; + 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 */ + + 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; + if (robot->energy < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + switch (MAP_GET_OBJECT (robot->map, x_to, y_to)) { + case SPACE: /* 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); + + user_interface_move_robot (robot->ui, robot->x, robot->y, x_to, y_to, robot->dir, robot->energy, robot->score, robot->shields); + + robot->x = x_to; + robot->y = y_to; + robot->units++; + + break; + + case BADDIE: /* baddie */ + /* Damage */ + + robot->shields -= 10; + if (robot->shields < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + return FALSE; + + break; + + case WALL: /* wall */ + /* less damage */ + + robot->shields -= 2; + if (robot->shields < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + return (FALSE); + + break; + + default: + /* even less damage */ + + if (--robot->shields < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + return (FALSE); + + break; + } + } /* for */ + + return (TRUE); +} + +gboolean +g_robot_smell (GRobot *robot, gchar *str) +{ + gint th; + gint i, j; + + g_return_val_if_fail (robot->ui != NULL && robot->map != NULL, FALSE); + + th = what_thing (str); + + /* no matter what, this took energy */ + + if (--robot->energy < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + user_interface_robot_smell (robot->ui, robot->x, robot->y, robot->dir, robot->energy, robot->score, robot->shields); + + /* 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 */ + + /* Failed to find it */ + + return (FALSE); +} + +gboolean +g_robot_feel (GRobot *robot, gchar *str) +{ + gint th; + gint x_to, y_to; + gint dx, dy; + + 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: /* 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; + } + + /* 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; + + user_interface_robot_feel ( + robot->ui, + robot->x, robot->y, + robot->dir, x_to, y_to, + robot->energy, robot->score, + robot->shields); + + 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_GET_OBJECT (robot->map, x_to, y_to) == th) { + return (TRUE); + } + + /* Did not feel it */ + return (FALSE); +} + +gboolean +g_robot_look (GRobot *robot, gchar *str) +{ + gint th; + gint x_to, y_to; + gint dx, dy; + + 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 0: /* N */ + dx = 0; + dy = -1; + break; + case 1: /* E */ + dx = 1; + dy = 0; + break; + case 2: /* S */ + dx = 0; + dy = 1; + break; + case 3: /* W */ + dx = -1; + dy = 0; + break; + } + + /* no matter what, this took energy */ + if (--robot->energy < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + /* Look for the thing */ + x_to = robot->x + dx; + y_to = robot->y + dy; + + user_interface_robot_look ( + robot->ui, + robot->x, robot->y, + robot->dir, x_to, y_to, + robot->energy, robot->score, + robot->shields); + + while (MAP_GET_OBJECT (robot->map, x_to, y_to) == SPACE) { + /* move the focus */ + x_to += dx; + y_to += dy; + } + + /* Outside the loop, we have found something */ + if (MAP_GET_OBJECT (robot->map, x_to, y_to) == th) { + return (TRUE); + } + + /* else, we did not find it */ + return (FALSE); +} + +gboolean +g_robot_grab (GRobot *robot) +{ + gint x_to, y_to; + gint dx, dy; + + 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 */ + + x_to = robot->x + dx; + y_to = robot->y + dy; + + robot->energy -= 5; + if (robot->energy < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + + user_interface_robot_grab (robot->ui, robot->x, robot->y, robot->dir, x_to, y_to, robot->energy, robot->score, robot->shields); + + /* Did we grab it? */ + + 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); + + case FOOD: + /* I want the net gain to be +10 */ + robot->energy += 15; + break; + + case PRIZE: + robot->score++; + break; + } + + /* only successful grabs get here */ + MAP_SET_OBJECT (robot->map, x_to, y_to, SPACE); + user_interface_add_thing (robot->ui, x_to, y_to, SPACE); + + return (TRUE); +} + +gboolean +g_robot_zap (GRobot *robot) +{ + gint x_to, y_to; + gint dx, dy; + + 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 zap the thing */ + + x_to = robot->x + dx; + y_to = robot->y + dy; + + robot->energy -= 10; + if (robot->energy < 1) { + g_signal_emit (robot, g_robot_signals[DEATH], 0); + } + robot->shots++; + user_interface_robot_zap (robot->ui, robot->x, robot->y, robot->dir, x_to, y_to, robot->energy, robot->score, robot->shields); + + /* 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: + user_interface_add_thing (robot->ui, x_to, y_to, SPACE); + break; + } + + /* only success gets here */ + MAP_SET_OBJECT (robot->map, x_to, y_to, SPACE); + user_interface_add_thing (robot->ui, x_to, y_to, SPACE); + + return (TRUE); +} + +gboolean +g_robot_stop (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); + + return (TRUE); /* never gets here */ +} + +static gint +what_thing (const gchar *th) +{ + /* what_thing - this function scans the list of possible things + (strings) and returns a cthing. Returns -1 if not found in the + list. */ + + /* 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. */ + + gint i; + + for (i = 0; i < 6; i++) { + if (strcmp (th, things[i]) == 0) { + return (cthings[i]); + } + } /* for */ + + /* not found */ + + return (-1); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ec6a581 --- /dev/null +++ b/src/main.c @@ -0,0 +1,565 @@ +/* $Id: main.c,v 1.28 2005/09/06 19:55:40 zeenix Exp $ */ +/* + GNU Robots game engine. This is the main() program, using GNU + Guile as my backend to handle the language. + + Copyright (C) 1998 Jim Hall, jhall1@isd.net + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include /* for getopt */ +#include /* for strdup */ + +#include +#include + +#include + +#include /* for GNU getopt_long */ +#include /* For loading our ui plugins */ + +#include "grobot.h" /* the robot structure, and robot manipulation routines */ +#include "api.h" /* robot API, as Scheme functions */ +#include "userinterface.h" +#include "config.h" +#include "configs.h" +#include "map.h" /* Game Map */ +#include "main.h" /* for this source file */ + +#define BUFF_LEN 1024 +#define MODULE_PREFIX "grobots-" +#define MODULE_PATH_MAX 256 +#define MODULE_NAME_MAX 256 + +/* Plugins we should know about STATICALLY */ +#define X11_MODULE "x11" +#define CURSES_MODULE "curses" + +/* Globals (share with api.c) */ +GList *robots = NULL; +GRobot *robot = NULL; // The current robot +UserInterface *ui; +Map *map; +GModule *plugin; + +UserInterface * load_ui_module (gchar *module_name, Map *map); +SCM catch_handler (void *data, SCM tag, SCM throw_args); + +/************************************************************************ + * main() * + * The program starts here! * + ************************************************************************/ + +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]; + + 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'}, + {"plugin", 1, NULL, 'p'}, + {NULL, 0, NULL, 0} + }; + + /* Initialize the GType system first */ + g_type_init (); + + /* Check command line */ + + /* Create a robot Object */ + robot = g_robot_new (1, 1, 1, 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 ("\n%s\n", PKGINFO); + g_printf ("%s\n", COPYRIGHT); + g_printf ("\nGNU 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 2 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, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n\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; + + case 'p': + + /* Set plugin */ + + main_argv[3] = optarg; /* pointer assignment */ + + 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] = ""; + } + + /* Start Guile environment. Does not exit */ + 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 0; /* never gets here, but keeps compiler + happy */ +} + +/************************************************************************ + * main_prog() * + * 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[]) +{ + gint i; + gchar *map_file = argv[1]; + gchar *robot_program = argv[2]; + gchar *module = argv[3]; + + 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_nicely (); + } + + /* ensure the robot is placed properly */ + MAP_SET_OBJECT (map, + G_ROBOT_POSITION_Y (robot), + G_ROBOT_POSITION_X (robot), + ROBOT); + + ui = load_ui_module (module, map); + + if (ui == NULL) { + exit_nicely (); + } + + /* Now initialize the rest of the Robot properties */ + g_object_set (G_OBJECT (robot), + "user-interface", G_OBJECT (ui), + "map", G_OBJECT (map), + NULL); + + g_signal_connect (G_OBJECT (robot), "death", G_CALLBACK (death), NULL); + + /* draw the map */ + user_interface_draw (ui); + + if (strlen (robot_program) != 0) { + /* execute a Scheme file */ + g_printf ("Robot program: %s\n", robot_program); + scm_c_primitive_load (robot_program); + } + + else { + gchar buff[BUFF_LEN]; + SCM value; + + g_printf ("Robot program not specified. Entering interactive mode..\n"); + while (1) { + user_interface_get_string (ui, "guile> ", buff, BUFF_LEN); + value = scm_internal_catch (SCM_BOOL_T, + (scm_t_catch_body) scm_c_eval_string, + (void *) buff, + catch_handler, + NULL); + } + } + + /* done */ + exit_nicely (); +} + +/************************************************************************ + * catch_handler (void *data, SCM tag, SCM throw_args); + * + * Responsible for handling errors + *************************************************************************/ + +SCM catch_handler (void *data, SCM tag, SCM throw_args) +{ + gchar *message = "Could'nt get error message\n"; + + if(scm_ilength (throw_args) > 1 && + SCM_NFALSEP (scm_string_p (SCM_CADR (throw_args)))) { + message = SCM_STRING_CHARS (SCM_CADR (throw_args)); + } + + else if (SCM_NFALSEP (scm_symbol_p (tag))) { + message = SCM_SYMBOL_CHARS (tag); + } + + user_interface_update_status (ui, message, -1, -1, -1); + + return SCM_BOOL_F; +} + +void death (GRobot *robot) +{ + /* We get a ref increment on a signal */ + g_object_unref (G_OBJECT (robot)); + + exit_nicely (); +} + +UserInterface * load_ui_module (gchar *module_name, Map *map) +{ + UserInterface *ui = NULL; + UserInterfaceInitFunc user_interface_new = NULL; + gchar module_full_name[MODULE_NAME_MAX]; + gchar module_path[MODULE_PATH_MAX]; + gchar *module_full_path; + gint errors = 0; + const char *path = getenv (MODULE_PATH_ENV); + + if (!g_module_supported ()) { + g_printf ("load_ui_module: %s\n", g_module_error ()); + return; + } + + if (path != NULL) { + g_strlcpy (module_path, path, MODULE_PATH_MAX); + } + + else { + g_strlcpy (module_path, MODULE_PATH, MODULE_PATH_MAX); + } + + /* Load the module. */ + g_strlcpy (module_full_name, MODULE_PREFIX, MODULE_NAME_MAX); + + if (module_name != NULL) { + g_strlcat (module_full_name, module_name, MODULE_NAME_MAX); + } + + else { + if (getenv ("DISPLAY") != NULL) { + /* Yuppi! we have x */ + g_strlcat (module_full_name, X11_MODULE, MODULE_NAME_MAX); + } + + else { + g_strlcat (module_full_name, CURSES_MODULE, MODULE_NAME_MAX); + } + } + + module_full_path = g_module_build_path (module_path, module_full_name); + plugin = g_module_open (module_full_path, 0); + g_free (module_full_path); + + /* Find our handles. */ + if (plugin) { + if (!(g_module_symbol (plugin, USER_INTERFACE_INIT_FUNCTION, (gpointer) &user_interface_new))) { + g_printf ("load_ui_module: %s\n", g_module_error ()); + g_module_close (plugin); + plugin = NULL; + } + + else { + ui = user_interface_new (map, user_interface_get_type ()); + } + } + + else { + g_printf ("error loading module '%s': %s\n", module_name, g_module_error ()); + } + + return ui; +} + +/************************************************************************ + * exit_nicely() * + * A function that allows the program to exit nicely, after freeing all * + * memory pointers, etc. * + ************************************************************************/ +void +exit_nicely () +{ + glong score, energy, shields, shots, units; + + /* Stop the UI */ + if (ui != NULL) { + g_object_unref (G_OBJECT (ui)); + } + + /* Get rid of the map object */ + if (map != NULL) { + g_object_unref (G_OBJECT (map)); + } + + /* Show statistics */ + g_object_get (G_OBJECT (robot), + "shields", &shields, + "energy", &energy, + "units", &units, + "shots", &shots, + "score", &score, + NULL); + + g_list_foreach (robots, g_object_unref, NULL); + g_list_free (robots); + + /* unload the plugin */ + if (plugin != NULL) { + g_module_close (plugin); + } + + 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"); + } + + /* Quit program */ + exit (0); +} + +/************************************************************************ + * usage() * + * A function that prints the usage of GNU Robots to the user. Assume * + * text mode for this function. We have not initialized X Windows or * + * curses 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 (this option is required)\n"); + g_printf (" -p, --plugin=PLUGIN Use plugin PLUGIN\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 (" an interactive mode if it is not specified.\n"); + + g_printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT); +} + +/************************************************************************ + * is_file_readable () * + * 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; + + stream = fopen (filename, "r"); + if (stream == NULL) { + /* Failed */ + + return (0); + } + + /* Success */ + + fclose (stream); + return (1); +} diff --git a/src/map.c b/src/map.c new file mode 100644 index 0000000..bae21dc --- /dev/null +++ b/src/map.c @@ -0,0 +1,297 @@ +/* GNU Robots. */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include /* for getopt */ +#include +#include /* for strdup */ + +#include + +#include "configs.h" +#include "map.h" + +enum +{ + ARG_0, + ARG_SIZE +}; + +GType _map_type = 0; + +static void map_class_init (MapClass * klass); +static void map_init (GObject * object); + +static void map_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void map_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GObjectClass *parent_class = NULL; + +GType +map_get_type (void) +{ + if (!_map_type) { + static const GTypeInfo object_info = { + sizeof (MapClass), + NULL, + NULL, + (GClassInitFunc) map_class_init, + NULL, + NULL, + sizeof (Map), + 0, + (GInstanceInitFunc) map_init, + NULL + }; + + _map_type = + g_type_register_static (G_TYPE_OBJECT, + "Map", + &object_info, + 0); + } + + return _map_type; +} + +static void +map_class_init (MapClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->set_property = map_set_property; + gobject_class->get_property = map_get_property; + + 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)); +} + +static void +map_init (GObject * object) +{ + Map *map = MAP (object); + + map->_map = NULL; + map->size.num_rows = -1; + map->size.num_cols = -1; +} + +/* fload_map - loads a map file into memory. */ +static void +fload_map (Map *map, FILE * stream) +{ + gint ch; + gint i, j; + + /* Read the map file */ + i = 0; + j = 0; + + 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; + + case '\n': + /* ignore the newline */ + break; + + default: + /* not a valid map char, but we'll add a space anyway */ + + map->_map[j][i] = SPACE; + break; + } /* switch */ + + /* Check for newline */ + + if (ch == '\n') { + /* the line may have ended early. pad with WALL */ + + while (i < map->size.num_cols) { + map->_map[j][i++] = WALL; + } + } + + + /* if newline */ + /* Check for limits on map */ + if (++i >= map->size.num_cols) { + /* We have filled this line */ + + ++j; + i = 0; + + /* Flush the buffer for this line */ + + while (ch != '\n') { + ch = fgetc (stream); + } + } + /* if i */ + if (j >= map->size.num_rows) { + /* the map is filled */ + + return; + } + } /* while fgetc */ + + /* After the loop, we are done reading the map file. Make sure this + is bounded by WALL. */ + if (i > 0) { + ++j; + } + + for (i = 0; i < map->size.num_cols; i++) { + map->_map[j][i] = WALL; + } +} + +static void +cleanup_map (Map *map) +{ + gint i, j; + + /* 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; + } + + /* 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; + } +} + +static void +map_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + Map *map; + guint i; + + /* 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 ARG_SIZE: + /* Free the map if any */ + if (map->_map != NULL) { + for (i = 0; i < map->size.num_rows; i++) { + g_free (map->_map[i]); + } + + g_free (map->_map); + } + + /* Get the new size of the map */ + g_memmove (&map->size, g_value_get_pointer (value), sizeof (MapSize)); + + /* Allocate the according to the new size */ + map->_map = g_malloc (map->size.num_rows * sizeof (gint **)); + + for (i = 0; i < map->size.num_rows; i++) { + map->_map[i] = g_malloc (map->size.num_cols * sizeof (gint)); + } + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +map_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + 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 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; + } +} + +Map * +map_new_from_file (const gchar *map_file, gint num_rows, gint num_cols) +{ + GObject *object; + Map *map; + gint **buf; + guint i; + FILE *stream; + MapSize size; + + size.num_rows = num_rows; + size.num_cols = num_cols; + + stream = fopen (map_file, "r"); + + if (stream != NULL) { + map = MAP (g_object_new (map_get_type (), + "size", &size, + NULL)); + fload_map (map, stream); + fclose (stream); + cleanup_map (map); + + return map; + } + + /* else, no file to open */ + else { + g_warning ("Could not open map file: %s\n", map_file); + return NULL; + } +} + diff --git a/src/sign.c b/src/sign.c new file mode 100644 index 0000000..07c9d7a --- /dev/null +++ b/src/sign.c @@ -0,0 +1,16 @@ +#include +#include + +/* is there a standard C function to do this? */ + +gint +sign (gint n) +{ + if (n < 0) { + return (-1); + } else if (n > 0) { + return (+1); + } + + return (0); +} diff --git a/src/userinterface.c b/src/userinterface.c new file mode 100644 index 0000000..c55f64f --- /dev/null +++ b/src/userinterface.c @@ -0,0 +1,192 @@ +/* $Id: userinterface.c,v 1.7 2004/10/21 19:24:30 zeenix Exp $ */ + +/* GNU Robots game engine. This is the User Interface module */ + +/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */ + +/* + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include "configs.h" +#include "userinterface.h" + +/*enum +{ + ARG_0, + ARG_MAP +};*/ + +GType _user_interface_type = 0; + +static void user_interface_base_init (UserInterfaceClass * klass); + +GType +user_interface_get_type (void) +{ + if (!_user_interface_type) { + static const GTypeInfo interface_info = { + sizeof (UserInterfaceClass), + (GBaseInitFunc) user_interface_base_init, + NULL, + NULL, + NULL, + NULL, + 0, + 0, + NULL, + NULL + }; + + _user_interface_type = g_type_register_static (G_TYPE_INTERFACE, + "UserInterface", + &interface_info, + 0); + } + + return _user_interface_type; +} + +static void +user_interface_base_init (UserInterfaceClass * klass) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + /*GObjectClass *gobject_class = + g_type_class_peek (((GTypeInterface *) klass)->g_instance_type); + + g_object_class_install_property (gobject_class, ARG_MAP,*/ + g_object_interface_install_property ( + klass, + g_param_spec_object ("map", + "Map", + "Reference to the Game Map object", + G_TYPE_MAP, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + initialized = TRUE; + } +} + +void user_interface_add_thing (UserInterface *ui, + gint x, + gint y, + gint thing) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_add_thing (ui, x, y, thing); +} + +void user_interface_draw (UserInterface *ui) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_draw (ui); +} + +void user_interface_move_robot (UserInterface *ui, + gint from_x, + gint from_y, + gint to_x, + gint to_y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_move_robot (ui, from_x, from_y, to_x, to_y, cdir, energy, score, shields); +} + +/* user_interfaces to animate the robot */ + void user_interface_robot_smell (UserInterface *ui, + gint x, + gint y, + gint cdir, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_robot_smell (ui, x, y, cdir, energy, score, shields); +} + +void user_interface_robot_zap (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_robot_zap (ui, x, y, cdir, x_to, y_to, energy, score, shields); +} + +void user_interface_robot_feel (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_robot_feel (ui, x, y, cdir, x_to, y_to, energy, score, shields); +} + +void user_interface_robot_grab (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_robot_grab (ui, x, y, cdir, x_to, y_to, energy, score, shields); +} + +void user_interface_robot_look (UserInterface *ui, + gint x, + gint y, + gint cdir, + gint x_to, + gint y_to, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_robot_look (ui, x, y, cdir, x_to, y_to, energy, score, shields); +} + +/* user_interfaces to get/display data from/to user */ +void user_interface_get_string (UserInterface *ui, + gchar *prompt, + gchar *buff, + gint len) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_get_string (ui, prompt, buff, len); +} + +void user_interface_update_status (UserInterface *ui, + const gchar *s, + glong energy, + glong score, + glong shields) +{ + USER_INTERFACE_GET_CLASS (ui)->user_interface_update_status (ui, s, energy, score, shields); +} + -- cgit v1.1