diff options
author | Bradley Smith | 2008-01-21 00:16:45 +0000 |
---|---|---|
committer | Bradley Smith | 2008-01-21 00:16:45 +0000 |
commit | e6e7222d5a730368ed4e84c2e0f55427460e5230 (patch) | |
tree | e608410401099ccebe7ffa21de9336d78c78efc9 | |
download | gnurobots-e6e7222d5a730368ed4e84c2e0f55427460e5230.tar.gz |
Imported GNU robots from CVS.
Signed-off-by: Bradley Smith <brad@brad-smith.co.uk>
65 files changed, 9478 insertions, 0 deletions
@@ -0,0 +1,9 @@ + +Authors of GNU Robots +===================== + +Daniel M. B. Fortes Manoel <dmbfm2@uol.com.br> +James Hall <jhall@gnu.org> +Tim Northover <tim@pnorthover.freeserve.co.uk> +Zeeshan Ali Khattak <zeenix@gmail.com> + @@ -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 <jhall1@isd.net> + +---------------------------------------- + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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. + + <signature of Ty Coon>, 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 <zeenix@gmail.com> + * src/main.c src/api.c include/api.h include/main.h: + Converted to the newer scm_* API. + +2004-10-22 Zeeshan Ali Khattak <zeenix@gmail.com> + * 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 <zeenix@gmail.com> + * 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 <zeenix@gmail.com> + + * 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 <zeenix@gmail.com> + + * 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 <zeenix@gmail.com> + + * 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 <zeenix@gmail.com> + + * 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 <zeenix@gmail.com> + + 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * include/ui.h src/ui.c: + Some changes to the UI object. + +2004-07-20 Zeeshan Ali Khattak <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * configure.in Makefile.am lib/Makefile.am: + Plugins are now compiled CONDITIONALLY. + +2004-07-17 Zeeshan Ali Khattak <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * configure.ac lib/Makefile.am src/Makefile.am: + Start to use libtool. + +2004-07-01 Zeeshan Ali Khattak <zak147@yahoo.com> + + * *.h *.c: + Some minor chages and first phase of GLIBization. + +2004-06-30 Zeeshan Ali Khattak <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * 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 <zak147@yahoo.com> + + * *.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 + @@ -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 <uzturnau@cyf-kr.edu.pl> 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 + <steinarh@stud.fim.ntnu.no> 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. @@ -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 <jhall1@isd.net> + + +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! + @@ -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 <dmbfm2@uol.com.br> +David Madore <david.madore@ens.fr> +Pawel Turnau <uzturnau@cyf-kr.edu.pl> +Steinar Hamre <steinarh@stud.fim.ntnu.no> +Tim Northover <tim@pnorthover.freeserve.co.uk> +Zeeshan Ali Khattak <zeenix@gmail.com> + @@ -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 <gtk/gtk.h> /* xwindows functions */ +#include <stdio.h> /* for file functions */ +#include <stdlib.h> /* 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 <jhall1@isd.net> + +_____________________________________________________________________ + +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, <jhall1@isd.net> + author + +* Tom Whittock, <shallow@dial.pipex.com> + X11 interface + +* Tim Northover, tim@pnorthover.freeserve.co.uk + mapedit.c (GNU Robots map editor, for GNOME) + +* Kyle Hasselbacher <kyle@toehold.com> + 'greedy.scm' and 'mapper.scm' + +* david.madore@ens.fr + Helped fix `configure.in' to detect X Windows + +* Steinar Hamre <steinarh@stud.fim.ntnu.no> + 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 <alloca.h> +#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 <stdio.h>. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include <stdio.h> + +/* 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 <stdlib.h> +#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 <string.h> +#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 <stdio.h> + +/* 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 <stdlib.h> +#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 <stdio.h> + +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 <glib.h> +#include <guile/gh.h> /* GNU Guile high */ + +void api_init (void); + +/* Functions */ +SCM api_robot_turn (SCM s_n); +SCM api_robot_move (SCM s_n); +SCM api_robot_smell (SCM s_th); +SCM api_robot_feel (SCM s_th); +SCM api_robot_look (SCM s_th); +SCM api_robot_grab (void); +SCM api_robot_zap (void); +SCM api_robot_stop (void); + +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 <jhall1@isd.net>" + +#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 <glib-object.h> +#include <glib.h> +#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 <glib.h> +#include <grobot.h> + +void main_prog (void *closure, gint argc, gchar *argv[]); +void death (GRobot *robot); +void exit_nicely (void); +void usage (const gchar *argv0); +gint is_file (const gchar *filename); diff --git a/include/map.h b/include/map.h 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 <glib-object.h> +#include <glib.h> + +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 <glib.h> + +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 <glib-object.h> +#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 <glib.h> +#include <stdio.h> +#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 <glib-object.h> +#include <curses.h> +#include <stdio.h> +#include <stdlib.h> /* 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 <glib.h> +#include <stdio.h> +#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 <glib-object.h> +#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 <glib.h> +#include <stdio.h> +#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 <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/xpm.h> +#include <X11/keysym.h> + +#include <glib-object.h> +#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 <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> + + 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 <jhall1@isd.net> +;;; 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 <kyle@toehold.com> +;;; + +;;; 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 <kyle@toehold.com> +; + +; 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 <jhall1@isd.net> +;;; 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 <jhall1@isd.net> +;;; 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 <jhall1@isd.net> +;;; 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 <stdio.h> +#include <stdlib.h> /* for abs, free */ + +#include <glib.h> +#include <libguile.h> /* 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 <stdio.h> +#include <glib.h> + +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 <stdio.h> +#include <unistd.h> /* for getopt */ +#include <string.h> /* for strdup */ + +#include <glib.h> +#include <gmodule.h> + +#include <libguile.h> + +#include <getopt.h> /* for GNU getopt_long */ +#include <ltdl.h> /* 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 <unistd.h> /* for getopt */ +#include <stdio.h> +#include <string.h> /* for strdup */ + +#include <glib.h> + +#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 <stdio.h> +#include <glib.h> + +/* 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 <glib.h> + +#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); +} + |