]> git.deb.at Git - pkg/netris.git/commitdiff
Imported Upstream version 0.4 upstream/0.4
authorGerfried Fuchs <rhonda@debian.at>
Wed, 14 Jul 2010 19:37:25 +0000 (21:37 +0200)
committerGerfried Fuchs <rhonda@debian.at>
Wed, 14 Jul 2010 19:37:25 +0000 (21:37 +0200)
15 files changed:
COPYING [new file with mode: 0644]
Configure [new file with mode: 0755]
FAQ [new file with mode: 0644]
README [new file with mode: 0644]
VERSION [new file with mode: 0644]
board.c [new file with mode: 0644]
curses.c [new file with mode: 0644]
game.c [new file with mode: 0644]
inet.c [new file with mode: 0644]
netris.h [new file with mode: 0644]
robot.c [new file with mode: 0644]
robot_desc [new file with mode: 0644]
shapes.c [new file with mode: 0644]
sr.c [new file with mode: 0644]
util.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..e77696a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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/Configure b/Configure
new file mode 100755 (executable)
index 0000000..7fe293e
--- /dev/null
+++ b/Configure
@@ -0,0 +1,259 @@
+:
+#
+# Netris -- A free networked version of T*tris
+# Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+# 
+# 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.
+#
+# $Id: Configure,v 1.17 1996/02/09 08:22:03 mhw Exp $
+#
+
+CC="gcc"
+COPT="-g -O"
+CEXTRA=""
+LEXTRA=""
+
+while [ $# -ge 1 ]; do
+       opt="$1"
+       shift
+       case "$opt" in
+               -g)
+                       COPT="-g"
+                       CEXTRA="-Wall -Wstrict-prototypes"
+                       ;;
+               -O*)
+                       COPT="$opt"
+                       CEXTRA="-DNDEBUG"
+                       ;;
+               --cc)
+                       CC="$1"
+                       shift
+                       ;;
+               --copt)
+                       COPT="$1"
+                       shift
+                       ;;
+               --cextra)
+                       CEXTRA="$1"
+                       shift
+                       ;;
+               --lextra)
+                       LEXTRA="$1"
+                       shift
+                       ;;
+               *)
+                       cat << "END"
+Usage: ./Configure [options...]
+    -g: Full debugging, no optimization, and full warnings
+    -O?: Optimization, no debugging or warnings
+    --cc <compiler>: Set the C compiler to use (default "gcc")
+    --copt <opt>: Set C optimization flags
+    --cextra <opt>: Set extra C flags
+    --lextra <opt>: Set extra linker flags
+END
+                       exit 1
+                       ;;
+       esac
+done
+
+CFLAGS="$COPT $CEXTRA"
+
+echo "Checking for libraries"
+echo 'main(){}' > test.c
+LFLAGS=""
+for lib in -lsocket -lnsl -lcurses -ltermcap
+do
+       if $CC $CFLAGS $LEXTRA test.c $lib > /dev/null 2>&1; then
+               LFLAGS="$LFLAGS $lib"
+       fi
+done
+
+echo "Checking for on_exit()"
+cat << END > test.c
+void handler(void) {}
+main() { on_exit(handler, (void *)0); }
+END
+if $CC $CFLAGS $LEXTRA test.c > /dev/null 2>&1; then
+       HAS_ON_EXIT=true
+else
+       HAS_ON_EXIT=false
+fi
+
+echo "Checking for sigprocmask()"
+cat << END > test.c
+#include <signal.h>
+main() { sigset_t set; sigprocmask(SIG_BLOCK, &set, &set); }
+END
+if $CC $CFLAGS $LEXTRA test.c > /dev/null 2>&1; then
+       HAS_SIGPROCMASK=true
+else
+       HAS_SIGPROCMASK=false
+fi
+
+echo "Checking for getopt.h"
+cat << END > test.c
+#include <getopt.h>
+main(){}
+END
+
+if $CC $CFLAGS $LEXTRA test.c > /dev/null 2>&1; then
+       HAS_GETOPT_H=true
+else
+       HAS_GETOPT_H=false
+fi
+
+echo "Checking for memory.h"
+cat << END > test.c
+#include <memory.h>
+main(){}
+END
+
+if $CC $CFLAGS $LEXTRA test.c > /dev/null 2>&1; then
+       HAS_MEMORY_H=true
+else
+       HAS_MEMORY_H=false
+fi
+
+rm -f test.c test.o a.out
+
+ORIG_SOURCES="game- curses- shapes- board- util- inet- robot-"
+GEN_SOURCES="version-"
+SOURCES="$ORIG_SOURCES $GEN_SOURCES"
+
+SRCS="`echo $SOURCES | sed -e s/-/.c/g`"
+OBJS="`echo $SOURCES | sed -e s/-/.o/g`"
+
+DISTFILES="README FAQ COPYING VERSION Configure netris.h sr.c robot_desc"
+DISTFILES="$DISTFILES `echo $ORIG_SOURCES | sed -e s/-/.c/g`"
+
+echo > .depend
+
+echo "Creating Makefile"
+sed -e "s/-LFLAGS-/$LFLAGS/g" -e "s/-SRCS-/$SRCS/g" \
+       -e "s/-OBJS-/$OBJS/g" -e "s/-DISTFILES-/$DISTFILES/g" \
+       -e "s/-COPT-/$COPT/g" -e "s/-CEXTRA-/$CEXTRA/g" \
+       -e "s/-LEXTRA-/$LEXTRA/g" -e "s/-CC-/$CC/g" << "END" > Makefile
+#
+# Automatically generated by ./Configure -- DO NOT EDIT!
+#
+
+CC = -CC-
+COPT = -COPT-
+CEXTRA = -CEXTRA-
+LEXTRA = -LEXTRA-
+LFLAGS = -LEXTRA- -LFLAGS-
+CFLAGS = $(CEXTRA) $(COPT)
+
+PROG = netris
+HEADERS = netris.h
+
+SRCS = -SRCS-
+OBJS = -OBJS-
+DISTFILES = -DISTFILES-
+
+all: Makefile config.h proto.h $(PROG) sr
+
+$(PROG): $(OBJS)
+       $(CC) -o $(PROG) $(OBJS) $(LFLAGS)
+
+sr: sr.o
+       $(CC) -o sr sr.o $(LFLAGS)
+
+.c.o:
+       $(CC) $(CFLAGS) -c $<
+
+Makefile config.h: Configure
+       @echo "Makefile and/or config.h is out of date"
+       @echo "Run ./Configure now"
+       @false
+
+version.c: VERSION
+       @echo "Creating version.c"
+       @sed -e 's/^\(.*\)$$/char *version_string = "\1";/' VERSION > $@
+
+proto.h: $(SRCS)
+       @touch $@
+       @mv $@ $@.old
+       @cat $(SRCS) | grep '^ExtFunc[  ]' | sed -e 's/)$$/);/' > $@
+       @if diff $@.old $@ > /dev/null 2>&1; then :; else \
+               echo "proto.h changed"; \
+               touch proto.chg; \
+       fi
+       @rm -f $@.old
+
+depend: proto.h $(SRCS)
+       @echo "Checking dependencies"
+       @sed -n -e '1,/make depend #####$$/p' Makefile > Makefile.new
+       @$(CC) -M $(SRCS) | sed -e 's/proto\.h/proto.chg/g' >> Makefile.new
+       @mv -f Makefile.new Makefile
+
+dist: $(DISTFILES)
+       @vers=`cat VERSION`; \
+       dir="netris-$$vers"; \
+       echo "Creating $$dir directory"; \
+       rm -rf $$dir; \
+       mkdir $$dir; \
+       cp $(DISTFILES) $$dir; \
+       chmod 755 $$dir; \
+       chmod 644 $$dir/*; \
+       chmod 755 $$dir/Configure; \
+       echo "Creating $$dir.tar.gz"; \
+       tar -cvzof $$dir.tar.gz $$dir
+
+clean:
+       rm -f proto.h proto.chg $(PROG) $(OBJS) version.c test.c a.out sr sr.o
+
+cleandir: clean
+       rm -f .depend Makefile config.h
+
+##### DO NOT EDIT OR DELETE THIS LINE, it's needed by make depend #####
+END
+
+echo "Creating config.h"
+cat << END > config.h
+/*
+ * Automatically generated by ./Configure -- DO NOT EDIT!
+ */
+
+END
+
+if [ "$HAS_GETOPT_H" = "true" ]; then
+       echo "#include <getopt.h>" >> config.h
+else
+       echo "extern char *optarg;" >> config.h
+       echo "extern int optind;" >> config.h
+fi
+if [ "$HAS_MEMORY_H" = "true" ]; then
+       echo "#include <memory.h>" >> config.h
+fi
+if [ "$HAS_ON_EXIT" = "true" ]; then
+       echo "#define HAS_ON_EXIT" >> config.h
+fi
+if [ "$HAS_SIGPROCMASK" = "true" ]; then
+       echo "#define HAS_SIGPROCMASK" >> config.h
+fi
+
+echo "Running 'make depend'"
+if make depend; then :; else cat << END; fi
+
+make depend failed, but that's OK unless you're doing development
+END
+cat << END
+
+Now do a 'make'
+
+END
+
+# vi: ts=4 ai
diff --git a/FAQ b/FAQ
new file mode 100644 (file)
index 0000000..18a203c
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,112 @@
+#
+# Netris
+# Frequently asked questions
+#
+# $Id: FAQ,v 1.3 1996/02/09 08:47:23 mhw Exp $
+#
+
+Questions
+=========
+[1] Where can I find the latest version?
+[2] The pieces look bizarre in my xterm window, and don't erase
+    properly.  What's up?
+[3] If I drop a piece and then slide it off a cliff, shouldn't
+    it automatically drop again?
+[4] When I try to play a networked game, it just hangs.
+[5] Is the game fair?  Is there an advantage to being the -w or the
+    -c player?
+[6] I'm using a slow terminal, and the game response is sluggish.
+    What can I do?
+[7] Why can't my terminal hide the cursor?
+
+Answers
+=======
+[1] Where can I find the latest version?
+
+    ftp://ftp.netris.org/pub/netris/
+
+    The latest version is available via anonymous ftp from
+    ftp.netris.org in /pub/netris.
+    
+    Unfortunately this machine is currently on the far end of a
+    14.4kbps modem connection, and may go away at any time without
+    notice, but I'll do my best to keep it up.  If you have trouble
+    contacting the ftp server, try mailing me at <mhw@netris.org>.
+
+[2] The pieces look bizarre in my xterm window, and don't erase
+    properly.  What's up?
+
+    Try disabling standout mode with the -S option.  If this fixes it,
+    it's probably because the bold font on your xterm is set wrong.
+    It's a good idea to fix it, since the blocks look much nicer when
+    they're inverse.
+
+    If the blocks don't erase correctly and are drawn larger than normal
+    characters, check your .Xdefaults file.  If you set the "font", make
+    sure you also set the "boldFont" to something of the same size.  For
+    example, I use 6x10 font for xterms, and here are the relevant lines
+    from my xterm.
+
+    txterm*font:       6x10
+    txterm*boldFont:   6x10
+
+    Make sure you capitalize the F in "boldFont"
+
+[3] If I drop a piece and then slide it off a cliff, shouldn't
+    it automatically drop again?
+
+    Try the -D option.
+
+[4] When I try to play a networked game, it just hangs.
+
+    There are two possibilities.  First, versions 0.1d? are incompatible
+    with current versions.  This is unfortunate, but remember, those were
+    very developmental versions.  I needed to fix up the protocol, and
+    I didn't want a whole bunch of messy compatibility code (at least
+    not yet :-)  Ask your opponent to get the latest version.
+
+    The other possibility is that you've typed in the wrong hostname, or
+    there's a port number mismatch, if either of you used the -p option.
+
+[5] Is the game fair?  Is there an advantage to being the -w or the
+    -c player?
+
+    The game is fair.  The game is completely symmetric once the
+    connection is established.  Furthermore, a random number seed is
+    exchanged at the start of the game, so both players will get the
+    same pieces.
+
+    There is a built-in random number generator, so even if the C
+    library on your system has a non-standard generator, both
+    players will still get the same sequence.
+
+    There is no attempt to synchronize the start of the game
+    accurately for networks with high latency.  This should be fixed
+    at some point, but I doubt such a small head start makes much
+    difference for a game lasting several minutes.
+
+[6] I'm using a slow terminal, and the game response is sluggish.
+    What can I do?
+
+    Try the -S option.  This disables use of standout mode (bold/inverse),
+    which require control sequences to be sent twice (or more) per line.
+    Standout mode makes the pieces look much more like blocks, but can
+    make the game unplayable on a slow terminal.
+
+    Also, you can type 's' to toggle spying (updating your view of the
+    opponent's board).
+
+[7] Why can't my terminal hide the cursor?
+
+    Netris uses the termcap library to look up the "vi" and "ve"
+    capabilities, which make the cursor invisible/visible
+    respectively.  These capabilities aren't very consistently
+    reported among un*xes, so I use compiled-in vt220 codes for
+    several vt100-like terminals.  Most emulators probably won't
+    support the codes, but they'll probably ignore them quietly.
+
+    Try setting the TERM environment variable to "vt100" or "vt220"
+    before running Netris.  If that doesn't work, your terminal
+    probably doesn't support cursor invisibility.
+
+# vi: tw=70 ai
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..fc7d155
--- /dev/null
+++ b/README
@@ -0,0 +1,132 @@
+#
+# Netris -- A free networked version of T*tris
+# Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+# 
+# 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.
+#
+# $Id: README,v 1.20 1996/02/09 08:22:06 mhw Exp $
+#
+
+This is an unfinished developmental version of Netris, a free
+networked version of T*tris.  It is distributed under the terms
+of the GNU General Public License, which is described in the
+file "COPYING" included with this distribution.
+
+In order to compile Netris you will need gcc.  You may be able to
+compile it with another ANSI C compiler, but if you attempt this
+you are on your own.
+
+It's been built and tested on at least the following systems:
+
+    NetBSD 1.0, 1.1
+    Linux
+    SunOS 4.1.1, 4.1.3
+    Solaris 2.3, 2.4
+    HP-UX
+
+If Netris doesn't build on your favorite system "out-of-the-box",
+I encourage you to mail me context diffs to fix the problem so I
+can fold it into the next version.
+
+Netris should build cleanly on 64-bit systems such as the Alpha,
+although you might need to edit the definitions for netint{2,4},
+hton{2,4}, and ntoh{2,4} in netris.h.  Alpha users, please let me know
+how it goes, and send me diffs if needed!
+
+See the FAQ in this directory if you have any problems.
+
+
+NEW IN VERSION 0.4
+==================
+- Netris now attempts to make the cursor invisible for terminals that
+  support it.  Xterm no, vt220 yes, vt100 maybe?
+- Ctrl-L (by default) will now redraw the screen.
+- Various cleanup and documentation changes.
+
+
+INSTALLATION
+============
+1. Run "./Configure" to create a Makefile and config.h appropriate
+   for your system.  If you have problems running Configure with
+   your /bin/sh, try "bash Configure".
+2. Try "make"
+3. Make sure "./netris" works properly
+4. Copy "./netris" to the appropriate public directory
+
+Try "./Configure -h" for more options
+
+
+RUNNING
+=======
+To start a two-player game, do the following:
+ 1. Player 1 types "netris -w".  This means "wait for challenge".
+ 2. Player 2 types "netris -c <host>" where <host> is the hostname
+    of Player 1.  This means "challenge".
+
+To start a one-player game, run netris with no parameters.
+One-player mode is a tad boring at the moment, because it never
+gets any faster, and there's no scoring.  This will be rectified
+at some point.  For now, use the "f" key (by default) to make the
+game go faster.  Speedups cannot be reversed for the remainder of
+the game.
+
+Unlike standard T*tris, Netris gives you a little extra time after
+dropping a piece before it solidifies.  This allows you to slide the
+piece into a notch without waiting for it to fall the whole way down.
+In fact, if you can even slide it off a cliff and it'll start falling
+again.  If you think it should automatically drop again in this case,
+use the -D option.
+
+The keys are:
+ 'j'    left
+ 'k'    rotate
+ 'l'    right
+ Space drop
+ 'm'    down faster
+ 's'   toggle spying on the other player
+ 'p'   pause
+ 'f'   make game faster (irreversable)
+ Ctrl-L        redraw the screen
+
+To see usage information, type "netris -h".
+To see distribution/warranty information, type "netris -H".
+To see the rules, type "netris -R".
+To use a port number other than the default, use the -p option.
+
+You can remap the keys with "-k <keys>", where <keys> is a string
+containing the keys in the order listed above.  The default is:
+    netris -k "jkl mspf^l"
+
+You needn't specify all of the keys, for example -k "asd" will only
+change the main three keys.  "^x" notation can be used for control
+characters.
+
+The "m" key moves the falling piece down one block, in addition to the
+usual step-down timer.  Use this in repetition when "drop" would go
+too far but you don't want to wait for the piece of fall.
+
+
+RUMORS
+======
+At some point I may implement a server that Netris players can connect
+to to find other players with similar skill across the globe.
+
+This version at least partially supports robots.  A rough description
+of the protocol is in "robot_desc", and a sample robot is in sr.c.
+
+The source code should be viewed with tab stops set every 4 columns,
+eg, "less -x4 game.c".
+
+# vi: tw=70 ai
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..bd73f47
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.4
diff --git a/board.c b/board.c
new file mode 100644 (file)
index 0000000..125cdd0
--- /dev/null
+++ b/board.c
@@ -0,0 +1,247 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: board.c,v 1.14 1996/02/09 08:22:08 mhw Exp $
+ */
+
+#include "netris.h"
+#include <stdlib.h>
+
+#ifdef DEBUG_FALLING
+# define B_OLD
+#else
+# define B_OLD abs
+#endif
+
+static BlockType board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+static BlockType oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
+static int falling[MAX_SCREENS][MAX_BOARD_WIDTH];
+static int oldFalling[MAX_SCREENS][MAX_BOARD_WIDTH];
+
+ExtFunc void InitBoard(int scr)
+{
+       boardHeight[scr] = MAX_BOARD_HEIGHT;
+       boardVisible[scr] = 20;
+       boardWidth[scr] = 10;
+       InitScreen(scr);
+}
+
+ExtFunc void CleanupBoard(int scr)
+{
+       CleanupScreen(scr);
+}
+
+ExtFunc BlockType GetBlock(int scr, int y, int x)
+{
+       if (y < 0 || x < 0 || x >= boardWidth[scr])
+               return BT_wall;
+       else if (y >= boardHeight[scr])
+               return BT_none;
+       else
+               return abs(board[scr][y][x]);
+}
+
+ExtFunc void SetBlock(int scr, int y, int x, BlockType type)
+{
+       if (y >= 0 && y < boardHeight[scr] && x >= 0 && x < boardWidth[scr]) {
+               if (y < boardVisible[scr])
+                       falling[scr][x] += (type < 0) - (board[scr][y][x] < 0);
+               board[scr][y][x] = type;
+               changed[scr][y] |= 1 << x;
+       }
+}
+
+ExtFunc int RefreshBoard(int scr)
+{
+       int y, x, any = 0;
+       unsigned int c;
+       BlockType b;
+
+       for (y = boardVisible[scr] - 1; y >= 0; --y)
+               if ((c = changed[scr][y])) {
+                       if (robotEnable) {
+                               RobotCmd(0, "RowUpdate %d %d", scr, y);
+                               for (x = 0; x < boardWidth[scr]; ++x) {
+                                       b = board[scr][y][x];
+                                       if (fairRobot)
+                                               b = abs(b);
+                                       RobotCmd(0, " %d", b);
+                               }
+                               RobotCmd(0, "\n");
+                       }
+                       changed[scr][y] = 0;
+                       any = 1;
+                       for (x = 0; c; (c >>= 1), (++x))
+                               if ((c & 1) && B_OLD(board[scr][y][x])!=oldBoard[scr][y][x]) {
+                                       PlotBlock(scr, y, x, B_OLD(board[scr][y][x]));
+                                       oldBoard[scr][y][x] = B_OLD(board[scr][y][x]);
+                               }
+               }
+       if (robotEnable)
+               RobotTimeStamp();
+       for (x = 0; x < boardWidth[scr]; ++x)
+               if (oldFalling[scr][x] != !!falling[scr][x]) {
+                       oldFalling[scr][x] = !!falling[scr][x];
+                       PlotUnderline(scr, x, oldFalling[scr][x]);
+                       any = 1;
+               }
+       return any;
+}
+
+ExtFunc int PlotFunc(int scr, int y, int x, BlockType type, void *data)
+{
+       SetBlock(scr, y, x, type);
+       return 0;
+}
+
+ExtFunc int EraseFunc(int scr, int y, int x, BlockType type, void *data)
+{
+       SetBlock(scr, y, x, BT_none);
+       return 0;
+}
+
+ExtFunc int CollisionFunc(int scr, int y, int x, BlockType type, void *data)
+{
+       return GetBlock(scr, y, x) != BT_none;
+}
+
+ExtFunc int VisibleFunc(int scr, int y, int x, BlockType type, void *data)
+{
+       return (y >= 0 && y < boardVisible[scr] && x >= 0 && x < boardWidth[scr]);
+}
+
+ExtFunc void PlotShape(Shape *shape, int scr, int y, int x, int falling)
+{
+       ShapeIterate(shape, scr, y, x, falling, PlotFunc, NULL);
+}
+
+ExtFunc void EraseShape(Shape *shape, int scr, int y, int x)
+{
+       ShapeIterate(shape, scr, y, x, 0, EraseFunc, NULL);
+}
+
+ExtFunc int ShapeFits(Shape *shape, int scr, int y, int x)
+{
+       return !ShapeIterate(shape, scr, y, x, 0, CollisionFunc, NULL);
+}
+
+ExtFunc int ShapeVisible(Shape *shape, int scr, int y, int x)
+{
+       return ShapeIterate(shape, scr, y, x, 0, VisibleFunc, NULL);
+}
+
+ExtFunc int MovePiece(int scr, int deltaY, int deltaX)
+{
+       int result;
+
+       EraseShape(curShape[scr], scr, curY[scr], curX[scr]);
+       result = ShapeFits(curShape[scr], scr, curY[scr] + deltaY,
+                               curX[scr] + deltaX);
+       if (result) {
+               curY[scr] += deltaY;
+               curX[scr] += deltaX;
+       }
+       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+       return result;
+}
+
+ExtFunc int RotatePiece(int scr)
+{
+       int result;
+
+       EraseShape(curShape[scr], scr, curY[scr], curX[scr]);
+       result = ShapeFits(curShape[scr]->rotateTo, scr, curY[scr], curX[scr]);
+       if (result)
+               curShape[scr] = curShape[scr]->rotateTo;
+       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+       return result;
+}
+
+ExtFunc int DropPiece(int scr)
+{
+       int count = 0;
+
+       EraseShape(curShape[scr], scr, curY[scr], curX[scr]);
+       while (ShapeFits(curShape[scr], scr, curY[scr] - 1, curX[scr])) {
+               --curY[scr];
+               ++count;
+       }
+       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+       return count;
+}
+
+ExtFunc int LineIsFull(int scr, int y)
+{
+       int x;
+
+       for (x = 0; x < boardWidth[scr]; ++x)
+               if (GetBlock(scr, y, x) == BT_none)
+                       return 0;
+       return 1;
+}
+
+ExtFunc void CopyLine(int scr, int from, int to)
+{
+       int x;
+
+       if (from != to)
+               for (x = 0; x < boardWidth[scr]; ++x)
+                       SetBlock(scr, to, x, GetBlock(scr, from, x));
+}
+
+ExtFunc int ClearFullLines(int scr)
+{
+       int from, to;
+
+       from = to = 0;
+       while (to < boardHeight[scr]) {
+               while (LineIsFull(scr, from))
+                       ++from;
+               CopyLine(scr, from++, to++);
+       }
+       return from - to;
+}
+
+ExtFunc void FreezePiece(int scr)
+{
+       int y, x;
+       BlockType type;
+
+       for (y = 0; y < boardHeight[scr]; ++y)
+               for (x = 0; x < boardWidth[scr]; ++x)
+                       if ((type = board[scr][y][x]) < 0)
+                               SetBlock(scr, y, x, -type);
+}
+
+ExtFunc void InsertJunk(int scr, int count, int column)
+{
+       int y, x;
+
+       for (y = boardHeight[scr] - count - 1; y >= 0; --y)
+               CopyLine(scr, y, y + count);
+       for (y = 0; y < count; ++y)
+               for (x = 0; x < boardWidth[scr]; ++x)
+                       SetBlock(scr, y, x, (x == column) ? BT_none : BT_piece1);
+       curY[scr] += count;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/curses.c b/curses.c
new file mode 100644 (file)
index 0000000..047efa4
--- /dev/null
+++ b/curses.c
@@ -0,0 +1,304 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: curses.c,v 1.32 1996/02/09 08:47:25 mhw Exp $
+ */
+
+#include "netris.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <curses.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void PlotBlock1(int scr, int y, int x, BlockType type);
+static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event);
+
+static EventGenRec keyGen =
+               { NULL, 0, FT_read, STDIN_FILENO, KeyGenFunc, EM_key };
+
+static int boardYPos[MAX_SCREENS], boardXPos[MAX_SCREENS];
+static int statusYPos, statusXPos;
+
+static char *term_vi;  /* String to make cursor invisible */
+static char *term_ve;  /* String to make cursor visible */
+
+ExtFunc void InitScreens(void)
+{
+       MySigSet oldMask;
+
+       GetTermcapInfo();
+
+       /*
+        * Do this atomically.  Otherwise a badly timed Ctrl-C during
+        * initialization will leave your terminal in a bad state.
+        */
+       BlockSignals(&oldMask, SIGINT, 0);
+       initscr();
+       AtExit(CleanupScreens);
+       RestoreSignals(NULL, &oldMask);
+
+       cbreak();
+       noecho();
+       OutputTermStr(term_vi, 0);
+       AddEventGen(&keyGen);
+
+       move(0, 0);
+       addstr("Netris ");
+       addstr(version_string);
+       addstr(" (C) 1994,1995,1996  Mark H. Weaver     "
+                       "\"netris -h\" for more info");
+       statusYPos = 22;
+       statusXPos = 0;
+}
+
+ExtFunc void CleanupScreens(void)
+{
+       RemoveEventGen(&keyGen);
+       endwin();
+       OutputTermStr(term_ve, 1);
+}
+
+ExtFunc void GetTermcapInfo(void)
+{
+       char *term, *buf, *data;
+       int bufSize = 10240;
+
+       if (!(term = getenv("TERM")))
+               return;
+       if (tgetent(scratch, term) == 1) {
+               /*
+                * Make the buffer HUGE, since tgetstr is unsafe.
+                * Allocate it on the heap too.
+                */
+               data = buf = malloc(bufSize);
+
+               /*
+                * There is no standard include file for tgetstr, no prototype
+                * definitions.  I like casting better than using my own prototypes
+                * because if I guess the prototype, I might be wrong, especially
+                * with regards to "const".
+                */
+               term_vi = (char *)tgetstr("vi", &data);
+               term_ve = (char *)tgetstr("ve", &data);
+
+               /* Okay, so I'm paranoid; I just don't like unsafe routines */
+               if (data > buf + bufSize)
+                       fatal("tgetstr overflow, you must have a very sick termcap");
+
+               /* Trim off the unused portion of buffer */
+               buf = realloc(buf, data - buf);
+       }
+
+       /*
+        * If that fails, use hardcoded vt220 codes.
+        * They don't seem to do anything bad on vt100's, so
+        * we'll try them just in case they work.
+        */
+       if (!term_vi || !term_ve) {
+               static char *vts[] = {
+                               "vt100", "vt101", "vt102",
+                               "vt200", "vt220", "vt300",
+                               "vt320", "vt400", "vt420",
+                               "screen", "xterm", NULL };
+               int i;
+
+               for (i = 0; vts[i]; i++)
+                       if (!strcmp(term, vts[i]))
+                       {
+                               term_vi = "\033[?25l";
+                               term_ve = "\033[?25h";
+                               break;
+                       }
+       }
+       if (!term_vi || !term_ve)
+               term_vi = term_ve = NULL;
+}
+
+ExtFunc void OutputTermStr(char *str, int flush)
+{
+       if (str) {
+               fputs(str, stdout);
+               if (flush)
+                       fflush(stdout);
+       }
+}
+
+ExtFunc void InitScreen(int scr)
+{
+       int y, x;
+
+       if (scr == 0)
+               boardXPos[scr] = 1;
+       else
+               boardXPos[scr] = boardXPos[scr - 1] +
+                                       2 * boardWidth[scr - 1] + 3;
+       boardYPos[scr] = 22;
+       if (statusXPos < boardXPos[scr] + 2 * boardWidth[scr] + 3)
+               statusXPos = boardXPos[scr] + 2 * boardWidth[scr] + 3;
+       for (y = boardVisible[scr] - 1; y >= 0; --y) {
+               move(boardYPos[scr] - y, boardXPos[scr] - 1);
+               addch('|');
+               move(boardYPos[scr] - y, boardXPos[scr] + 2 * boardWidth[scr]);
+               addch('|');
+       }
+       for (y = boardVisible[scr]; y >= -1; y -= boardVisible[scr] + 1) {
+               move(boardYPos[scr] - y, boardXPos[scr] - 1);
+               addch('+');
+               for (x = boardWidth[scr] - 1; x >= 0; --x)
+                       addstr("--");
+               addch('+');
+       }
+}
+
+ExtFunc void CleanupScreen(int scr)
+{
+}
+
+static void PlotBlock1(int scr, int y, int x, BlockType type)
+{
+       move(boardYPos[scr] - y, boardXPos[scr] + 2 * x);
+       switch (type) {
+               case BT_none:
+                       addstr("  ");
+                       break;
+               case -BT_piece1:
+                       if (standoutEnable)
+                               standout();
+                       addstr("$$");
+                       if (standoutEnable)
+                               standend();
+                       break;
+               case BT_piece1:
+                       if (standoutEnable)
+                               standout();
+                       addstr("[]");
+                       if (standoutEnable)
+                               standend();
+                       break;
+               default:
+                       assert(0);
+       }
+}
+
+ExtFunc void PlotBlock(int scr, int y, int x, BlockType type)
+{
+       if (y >= 0 && y < boardVisible[scr] && x >= 0 && x < boardWidth[scr])
+               PlotBlock1(scr, y, x, type);
+}
+
+ExtFunc void PlotUnderline(int scr, int x, int flag)
+{
+       move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x);
+       addstr(flag ? "==" : "--");
+}
+
+ExtFunc void ShowDisplayInfo(void)
+{
+       move(statusYPos - 9, statusXPos);
+       printw("Seed: %d", initSeed);
+       clrtoeol();
+       move(statusYPos - 8, statusXPos);
+       printw("Speed: %dms", speed / 1000);
+       clrtoeol();
+       if (robotEnable) {
+               move(statusYPos - 6, statusXPos);
+               if (fairRobot)
+                       addstr("Controlled by a fair robot");
+               else
+                       addstr("Controlled by a robot");
+               clrtoeol();
+       }
+       if (opponentFlags & SCF_usingRobot) {
+               move(statusYPos - 5, statusXPos);
+               if (opponentFlags & SCF_fairRobot)
+                       addstr("The opponent is a fair robot");
+               else
+                       addstr("The opponent is a robot");
+               clrtoeol();
+       }
+}
+
+ExtFunc void UpdateOpponentDisplay(void)
+{
+       move(1, 0);
+       printw("Playing %s@%s", opponentName, opponentHost);
+       clrtoeol();
+}
+
+ExtFunc void ShowPause(int pausedByMe, int pausedByThem)
+{
+       move(statusYPos - 3, statusXPos);
+       if (pausedByThem)
+               addstr("Game paused by opponent");
+       else
+               clrtoeol();
+       move(statusYPos - 2, statusXPos);
+       if (pausedByMe)
+               addstr("Game paused by you");
+       else
+               clrtoeol();
+}
+
+ExtFunc void Message(char *s)
+{
+       static int line = 0;
+
+       move(statusYPos - 20 + line, statusXPos);
+       addstr(s);      /* XXX Should truncate long lines */
+       clrtoeol();
+       line = (line + 1) % 10;
+       move(statusYPos - 20 + line, statusXPos);
+       clrtoeol();
+}
+
+ExtFunc void RefreshScreen(void)
+{
+       static char timeStr[2][32];
+       time_t theTime;
+
+       time(&theTime);
+       strftime(timeStr[0], 30, "%I:%M %p", localtime(&theTime));
+       /* Just in case the local curses library sucks */
+       if (strcmp(timeStr[0], timeStr[1]))
+       {
+               move(statusYPos, statusXPos);
+               addstr(timeStr[0]);
+               strcpy(timeStr[1], timeStr[0]);
+       }
+       move(boardYPos[0] + 1, boardXPos[0] + 2 * boardWidth[0] + 1);
+       refresh();
+}
+
+ExtFunc void ScheduleFullRedraw(void)
+{
+       touchwin(stdscr);
+}
+
+static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event)
+{
+       if (MyRead(gen->fd, &event->u.key, 1))
+               return E_key;
+       else
+               return E_none;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/game.c b/game.c
new file mode 100644 (file)
index 0000000..26ab6a8
--- /dev/null
+++ b/game.c
@@ -0,0 +1,534 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: game.c,v 1.38 1996/02/09 08:22:11 mhw Exp $
+ */
+
+#define NOEXT
+#include "netris.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+enum { KT_left, KT_rotate, KT_right, KT_drop, KT_down,
+       KT_toggleSpy, KT_pause, KT_faster, KT_redraw, KT_numKeys };
+
+static char *keyNames[KT_numKeys+1] = {
+       "Left", "Rotate", "Right", "Drop", "Down", "ToggleSpy", "Pause",
+       "Faster", "Redraw", NULL };
+
+static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
+
+static char keyTable[KT_numKeys+1];
+static int dropModeEnable = 0;
+static char *robotProg;
+
+ExtFunc void MapKeys(char *newKeys)
+{
+       int i, k, ch;
+       char used[256];
+       int errs = 0;
+
+       /* XXX assumptions about ASCII encoding here */
+       for (i = k = 0; newKeys[i] && k < KT_numKeys; i++,k++) {
+               if (newKeys[i] == '^' && newKeys[i+1])
+                       keyTable[k] = toupper(newKeys[++i]) - ('A' - 1);
+               else
+                       keyTable[k] = newKeys[i];
+       }
+       memset(used, 0, sizeof(used));
+       for (k = 0; k < KT_numKeys; k++) {
+               ch = (unsigned char) keyTable[k];
+               if (used[ch]) {
+                       if (iscntrl(ch) && ch < ' ')
+                               sprintf(scratch, "Ctrl-%c", ch + ('A' - 1));
+                       else if (isprint(ch))
+                               sprintf(scratch, "\"%c\"", ch);
+                       else
+                               sprintf(scratch, "0x%X", ch);
+                       if (!errs)
+                               fprintf(stderr, "Duplicate key mappings:\n");
+                       errs++;
+                       fprintf(stderr, "  %s mapped to both %s and %s\n",
+                                       scratch, keyNames[used[ch]-1], keyNames[k]);
+               }
+               used[ch] = k + 1;
+       }
+       if (errs)
+               exit(1);
+}
+
+ExtFunc int StartNewPiece(int scr, Shape *shape)
+{
+       curShape[scr] = shape;
+       curY[scr] = boardVisible[scr] + 4;
+       curX[scr] = boardWidth[scr] / 2;
+       while (!ShapeVisible(shape, scr, curY[scr], curX[scr]))
+               --curY[scr];
+       if (!ShapeFits(shape, scr, curY[scr], curX[scr]))
+               return 0;
+       PlotShape(shape, scr, curY[scr], curX[scr], 1);
+       return 1;
+}
+
+ExtFunc void OneGame(int scr, int scr2)
+{
+       MyEvent event;
+       int linesCleared, changed = 0;
+       int spied = 0, spying = 0, dropMode = 0;
+       int oldPaused = 0, paused = 0, pausedByMe = 0, pausedByThem = 0;
+       long pauseTimeLeft;
+       int pieceCount = 0;
+       int key;
+       char *p, *cmd;
+
+       speed = stepDownInterval;
+       ResetBaseTime();
+       InitBoard(scr);
+       if (scr2 >= 0) {
+               spied = 1;
+               spying = 1;
+               InitBoard(scr2);
+               UpdateOpponentDisplay();
+       }
+       ShowDisplayInfo();
+       SetITimer(speed, speed);
+       if (robotEnable) {
+               RobotCmd(0, "GameType %s\n", gameNames[game]);
+               RobotCmd(0, "BoardSize 0 %d %d\n",
+                               boardVisible[scr], boardWidth[scr]);
+               if (scr2 >= 0) {
+                       RobotCmd(0, "BoardSize 1 %d %d\n",
+                                       boardVisible[scr2], boardWidth[scr2]);
+                       RobotCmd(0, "Opponent 1 %s %s\n", opponentName, opponentHost);
+                       if (opponentFlags & SCF_usingRobot)
+                               RobotCmd(0, "OpponentFlag 1 robot\n");
+                       if (opponentFlags & SCF_fairRobot)
+                               RobotCmd(0, "OpponentFlag 1 fairRobot\n");
+               }
+               RobotCmd(0, "TickLength %.3f\n", speed / 1.0e6);
+               RobotCmd(0, "BeginGame\n");
+               RobotTimeStamp();
+       }
+       while (StartNewPiece(scr, ChooseOption(stdOptions))) {
+               if (robotEnable && !fairRobot)
+                       RobotCmd(1, "NewPiece %d\n", ++pieceCount);
+               if (spied) {
+                       short shapeNum;
+                       netint2 data[1];
+
+                       shapeNum = ShapeToNetNum(curShape[scr]);
+                       data[0] = hton2(shapeNum);
+                       SendPacket(NP_newPiece, sizeof(data), data);
+               }
+               for (;;) {
+                       changed = RefreshBoard(scr) || changed;
+                       if (spying)
+                               changed = RefreshBoard(scr2) || changed;
+                       if (changed) {
+                               RefreshScreen();
+                               changed = 0;
+                       }
+                       CheckNetConn();
+                       switch (WaitMyEvent(&event, EM_any)) {
+                               case E_alarm:
+                                       if (!MovePiece(scr, -1, 0))
+                                               goto nextPiece;
+                                       else if (spied)
+                                               SendPacket(NP_down, 0, NULL);
+                                       break;
+                               case E_key:
+                                       p = strchr(keyTable, tolower(event.u.key));
+                                       key = p - keyTable;
+                                       if (robotEnable) {
+                                               RobotCmd(1, "UserKey %d %s\n",
+                                                               (int)(unsigned char)event.u.key,
+                                                               p ? keyNames[key] : "?");
+                                               break;
+                                       }
+                                       if (!p)
+                                               break;
+                               keyEvent:
+                                       if (paused && (key != KT_pause) && (key != KT_redraw))
+                                               break;
+                                       switch(key) {
+                                               case KT_left:
+                                                       if (MovePiece(scr, 0, -1) && spied)
+                                                               SendPacket(NP_left, 0, NULL);
+                                                       break;
+                                               case KT_right:
+                                                       if (MovePiece(scr, 0, 1) && spied)
+                                                               SendPacket(NP_right, 0, NULL);
+                                                       break;
+                                               case KT_rotate:
+                                                       if (RotatePiece(scr) && spied)
+                                                               SendPacket(NP_rotate, 0, NULL);
+                                                       break;
+                                               case KT_down:
+                                                       if (MovePiece(scr, -1, 0) && spied)
+                                                               SendPacket(NP_down, 0, NULL);
+                                                       break;
+                                               case KT_toggleSpy:
+                                                       spying = (!spying) && (scr2 >= 0);
+                                                       break;
+                                               case KT_drop:
+                                                       if (DropPiece(scr) > 0) {
+                                                               if (spied)
+                                                                       SendPacket(NP_drop, 0, NULL);
+                                                               SetITimer(speed, speed);
+                                                       }
+                                                       dropMode = dropModeEnable;
+                                                       break;
+                                               case KT_pause:
+                                                       pausedByMe = !pausedByMe;
+                                                       if (game == GT_classicTwo) {
+                                                               netint2 data[1];
+
+                                                               data[0] = hton2(pausedByMe);
+                                                               SendPacket(NP_pause, sizeof(data), data);
+                                                       }
+                                                       paused = pausedByMe || pausedByThem;
+                                                       if (robotEnable)
+                                                               RobotCmd(1, "Pause %d %d\n", pausedByMe,
+                                                                       pausedByThem);
+                                                       ShowPause(pausedByMe, pausedByThem);
+                                                       changed = 1;
+                                                       break;
+                                               case KT_faster:
+                                                       if (game != GT_onePlayer)
+                                                               break;
+                                                       speed = speed * 0.8;
+                                                       SetITimer(speed, SetITimer(0, 0));
+                                                       ShowDisplayInfo();
+                                                       changed = 1;
+                                                       break;
+                                               case KT_redraw:
+                                                       ScheduleFullRedraw();
+                                                       if (paused)
+                                                               RefreshScreen();
+                                                       break;
+                                       }
+                                       if (dropMode && DropPiece(scr) > 0) {
+                                               if (spied)
+                                                       SendPacket(NP_drop, 0, NULL);
+                                               SetITimer(speed, speed);
+                                       }
+                                       break;
+                               case E_robot:
+                               {
+                                       int num;
+
+                                       cmd = event.u.robot.data;
+                                       if ((p = strchr(cmd, ' ')))
+                                               *p++ = 0;
+                                       else
+                                               p = cmd + strlen(cmd);
+                                       for (key = 0; keyNames[key]; ++key)
+                                               if (!strcmp(keyNames[key], cmd) &&
+                                                               (fairRobot || (1 == sscanf(p, "%d", &num) &&
+                                                                       num == pieceCount)))
+                                                       goto keyEvent;
+                                       if (!strcmp(cmd, "Message")) {
+                                               Message(p);
+                                               changed = 1;
+                                       }
+                                       break;
+                               }
+                               case E_net:
+                                       switch(event.u.net.type) {
+                                               case NP_giveJunk:
+                                               {
+                                                       netint2 data[2];
+                                                       short column;
+
+                                                       memcpy(data, event.u.net.data, sizeof(data[0]));
+                                                       column = Random(0, boardWidth[scr]);
+                                                       data[1] = hton2(column);
+                                                       InsertJunk(scr, ntoh2(data[0]), column);
+                                                       if (spied)
+                                                               SendPacket(NP_insertJunk, sizeof(data), data);
+                                                       break;
+                                               }
+                                               case NP_newPiece:
+                                               {
+                                                       short shapeNum;
+                                                       netint2 data[1];
+
+                                                       FreezePiece(scr2);
+                                                       memcpy(data, event.u.net.data, sizeof(data));
+                                                       shapeNum = ntoh2(data[0]);
+                                                       StartNewPiece(scr2, NetNumToShape(shapeNum));
+                                                       break;
+                                               }
+                                               case NP_down:
+                                                       MovePiece(scr2, -1, 0);
+                                                       break;
+                                               case NP_left:
+                                                       MovePiece(scr2, 0, -1);
+                                                       break;
+                                               case NP_right:
+                                                       MovePiece(scr2, 0, 1);
+                                                       break;
+                                               case NP_rotate:
+                                                       RotatePiece(scr2);
+                                                       break;
+                                               case NP_drop:
+                                                       DropPiece(scr2);
+                                                       break;
+                                               case NP_clear:
+                                                       ClearFullLines(scr2);
+                                                       break;
+                                               case NP_insertJunk:
+                                               {
+                                                       netint2 data[2];
+
+                                                       memcpy(data, event.u.net.data, sizeof(data));
+                                                       InsertJunk(scr2, ntoh2(data[0]), ntoh2(data[1]));
+                                                       break;
+                                               }
+                                               case NP_pause:
+                                               {
+                                                       netint2 data[1];
+
+                                                       memcpy(data, event.u.net.data, sizeof(data));
+                                                       pausedByThem = ntoh2(data[0]);
+                                                       paused = pausedByMe || pausedByThem;
+                                                       if (robotEnable)
+                                                               RobotCmd(1, "Pause %d %d\n", pausedByMe,
+                                                                       pausedByThem);
+                                                       ShowPause(pausedByMe, pausedByThem);
+                                                       changed = 1;
+                                                       break;
+                                               }
+                                               default:
+                                                       break;
+                                       }
+                                       break;
+                               case E_lostRobot:
+                               case E_lostConn:
+                                       goto gameOver;
+                               default:
+                                       break;
+                       }
+                       if (paused != oldPaused) {
+                               if (paused)
+                                       pauseTimeLeft = SetITimer(0, 0);
+                               else
+                                       SetITimer(speed, pauseTimeLeft);
+                               oldPaused = paused;
+                       }
+               }
+       nextPiece:
+               dropMode = 0;
+               FreezePiece(scr);
+               linesCleared = ClearFullLines(scr);
+               if (linesCleared > 0 && spied)
+                       SendPacket(NP_clear, 0, NULL);
+               if (game == GT_classicTwo && linesCleared > 1) {
+                       short junkLines;
+                       netint2 data[1];
+
+                       junkLines = linesCleared - (linesCleared < 4);
+                       data[0] = hton2(junkLines);
+                       SendPacket(NP_giveJunk, sizeof(data), data);
+               }
+       }
+gameOver:
+       SetITimer(0, 0);
+}
+
+ExtFunc int main(int argc, char **argv)
+{
+       int initConn = 0, waitConn = 0, ch;
+       char *hostStr = NULL, *portStr = NULL;
+
+       standoutEnable = 1;
+       stepDownInterval = DEFAULT_INTERVAL;
+       MapKeys(DEFAULT_KEYS);
+       while ((ch = getopt(argc, argv, "hHRs:r:Fk:c:woDSp:i:")) != -1)
+               switch (ch) {
+                       case 'c':
+                               initConn = 1;
+                               hostStr = optarg;
+                               break;
+                       case 'w':
+                               waitConn = 1;
+                               break;
+                       case 'p':
+                               portStr = optarg;
+                               break;
+                       case 'i':
+                               stepDownInterval = atof(optarg) * 1e6;
+                               break;
+                       case 's':
+                               initSeed = atoi(optarg);
+                               myFlags |= SCF_setSeed;
+                               break;
+                       case 'r':
+                               robotEnable = 1;
+                               robotProg = optarg;
+                               myFlags |= SCF_usingRobot;
+                               break;
+                       case 'F':
+                               fairRobot = 1;
+                               myFlags |= SCF_fairRobot;
+                               break;
+                       case 'D':
+                               dropModeEnable = 1;
+                               break;
+                       case 'S':
+                               standoutEnable = 0;
+                               break;
+                       case 'k':
+                               MapKeys(optarg);
+                               break;
+                       case 'H':
+                               DistInfo();
+                               exit(0);
+                       case 'R':
+                               Rules();
+                               exit(0);
+                       case 'h':
+                               Usage();
+                               exit(0);
+                       default:
+                               Usage();
+                               exit(1);
+               }
+       if (optind < argc || (initConn && waitConn)) {
+               Usage();
+               exit(1);
+       }
+       if (fairRobot && !robotEnable)
+               fatal("You can't use the -F option without the -r option");
+       InitUtil();
+       if (robotEnable)
+               InitRobot(robotProg);
+       InitNet();
+       InitScreens();
+       if (initConn || waitConn) {
+               MyEvent event;
+
+               game = GT_classicTwo;
+               if (initConn)
+                       InitiateConnection(hostStr, portStr);
+               else if (waitConn)
+                       WaitForConnection(portStr);
+               {
+                       netint4 data[2];
+                       int major;
+
+                       data[0] = hton4(MAJOR_VERSION);
+                       data[1] = hton4(PROTOCOL_VERSION);
+                       SendPacket(NP_version, sizeof(data), data);
+                       if (WaitMyEvent(&event, EM_net) != E_net)
+                               fatal("Network negotiation failed");
+                       memcpy(data, event.u.net.data, sizeof(data));
+                       major = ntoh4(data[0]);
+                       protocolVersion = ntoh4(data[1]);
+                       if (event.u.net.type != NP_version || major < MAJOR_VERSION)
+                               fatal("Your opponent is using an old, incompatible version\n"
+                                       "of Netris.  They should get the latest version.");
+                       if (major > MAJOR_VERSION)
+                               fatal("Your opponent is using an newer, incompatible version\n"
+                                       "of Netris.  Get the latest version.");
+                       if (protocolVersion > PROTOCOL_VERSION)
+                               protocolVersion = PROTOCOL_VERSION;
+               }
+               if (protocolVersion < 3 && stepDownInterval != DEFAULT_INTERVAL)
+                       fatal("Your opponent's version of Netris predates the -i option.\n"
+                                       "For fairness, you shouldn't use the -i option either.");
+               {
+                       netint4 data[3];
+                       int len;
+                       int seed;
+
+                       if (protocolVersion >= 3)
+                               len = sizeof(data);
+                       else
+                               len = sizeof(netint4[2]);
+                       if ((myFlags & SCF_setSeed))
+                               seed = initSeed;
+                       else
+                               seed = time(0);
+                       if (waitConn)
+                               SRandom(seed);
+                       data[0] = hton4(myFlags);
+                       data[1] = hton4(seed);
+                       data[2] = hton4(stepDownInterval);
+                       SendPacket(NP_startConn, len, data);
+                       if (WaitMyEvent(&event, EM_net) != E_net ||
+                                       event.u.net.type != NP_startConn)
+                               fatal("Network negotiation failed");
+                       memcpy(data, event.u.net.data, len);
+                       opponentFlags = ntoh4(data[0]);
+                       seed = ntoh4(data[1]);
+                       if (initConn) {
+                               if ((opponentFlags & SCF_setSeed) != (myFlags & SCF_setSeed))
+                                       fatal("If one player sets the random number seed, "
+                                                       "both must.");
+                               if ((myFlags & SCF_setSeed) && seed != initSeed)
+                                       fatal("Both players have set the random number seed, "
+                                                       "and they are unequal.");
+                               if (protocolVersion >= 3 && stepDownInterval != ntoh4(data[2]))
+                                       fatal("Your opponent is using a different step-down "
+                                               "interval (-i).\nYou must both use the same one.");
+                               SRandom(seed);
+                       }
+               }
+               {
+                       char *userName;
+                       int len, i;
+
+                       userName = getenv("LOGNAME");
+                       if (!userName || !userName[0])
+                               userName = getenv("USER");
+                       if (!userName || !userName[0])
+                               strcpy(userName, "???");
+                       len = strlen(userName)+1;
+                       if (len > sizeof(opponentName))
+                               len = sizeof(opponentName);
+                       SendPacket(NP_userName, len, userName);
+                       if (WaitMyEvent(&event, EM_net) != E_net ||
+                                       event.u.net.type != NP_userName)
+                               fatal("Network negotiation failed");
+                       strncpy(opponentName, event.u.net.data,
+                               sizeof(opponentName)-1);
+                       opponentName[sizeof(opponentName)-1] = 0;
+                       for (i = 0; opponentName[i]; ++i)
+                               if (!isprint(opponentName[i]))
+                                       opponentName[i] = '?';
+                       for (i = 0; opponentHost[i]; ++i)
+                               if (!isprint(opponentHost[i]))
+                                       opponentHost[i] = '?';
+               }
+               OneGame(0, 1);
+       }
+       else {
+               game = GT_onePlayer;
+               OneGame(0, -1);
+       }
+       return 0;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/inet.c b/inet.c
new file mode 100644 (file)
index 0000000..dbfe748
--- /dev/null
+++ b/inet.c
@@ -0,0 +1,216 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: inet.c,v 1.18 1996/02/09 08:22:13 mhw Exp $
+ */
+
+#include "netris.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <errno.h>
+
+#define HEADER_SIZE sizeof(netint2[2])
+
+static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
+
+static int sock = -1;
+static EventGenRec netGen = { NULL, 0, FT_read, -1, NetGenFunc, EM_net };
+
+static char netBuf[64];
+static int netBufSize, netBufGoal = HEADER_SIZE;
+static int isServer, lostConn, gotEndConn;
+
+ExtFunc void InitNet(void)
+{
+       AtExit(CloseNet);
+}
+
+ExtFunc int WaitForConnection(char *portStr)
+{
+       struct sockaddr_in addr;
+       struct hostent *host;
+       int sockListen;
+       int addrLen;
+       short port;
+       int val1;
+       struct linger val2;
+
+       if (portStr)
+               port = atoi(portStr);   /* XXX Error checking */
+       else
+               port = DEFAULT_PORT;
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = htons(port);
+       sockListen = socket(AF_INET, SOCK_STREAM, 0);
+       if (sockListen < 0)
+               die("socket");
+       val1 = 1;
+       setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
+                       (void *)&val1, sizeof(val1));
+       if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+               die("bind");
+       if (listen(sockListen, 1) < 0)
+               die("listen");
+       addrLen = sizeof(addr);
+       sock = accept(sockListen, (struct sockaddr *)&addr, &addrLen);
+       if (sock < 0)
+               die("accept");
+       close(sockListen);
+       val2.l_onoff = 1;
+       val2.l_linger = 0;
+       setsockopt(sock, SOL_SOCKET, SO_LINGER,
+                       (void *)&val2, sizeof(val2));
+       netGen.fd = sock;
+       strcpy(opponentHost, "???");
+       if (addr.sin_family == AF_INET) {
+               host = gethostbyaddr((void *)&addr.sin_addr,
+                               sizeof(struct in_addr), AF_INET);
+               if (host) {
+                       strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
+                       opponentHost[sizeof(opponentHost)-1] = 0;
+               }
+       }
+       AddEventGen(&netGen);
+       isServer = 1;
+       return 0;
+}
+
+ExtFunc int InitiateConnection(char *hostStr, char *portStr)
+{
+       struct sockaddr_in addr;
+       struct hostent *host;
+       short port;
+       int mySock;
+
+       if (portStr)
+               port = atoi(portStr);   /* XXX Error checking */
+       else
+               port = DEFAULT_PORT;
+       host = gethostbyname(hostStr);
+       if (!host)
+               die("gethostbyname");
+       assert(host->h_addrtype == AF_INET);
+       strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
+       opponentHost[sizeof(opponentHost)-1] = 0;
+ again:
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = host->h_addrtype;
+       memcpy(&addr.sin_addr, host->h_addr, host->h_length);
+       addr.sin_port = htons(port);
+       mySock = socket(AF_INET, SOCK_STREAM, 0);
+       if (mySock < 0)
+               die("socket");
+       if (connect(mySock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               if (errno != ECONNREFUSED)
+                       die("connect");
+               close(mySock);
+               sleep(1);
+               goto again;
+       }
+       netGen.fd = sock = mySock;
+       AddEventGen(&netGen);
+       return 0;
+}
+
+static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
+{
+       int result;
+       short type, size;
+       netint2 data[2];
+
+       result = MyRead(sock, netBuf + netBufSize, netBufGoal - netBufSize);
+       if (result < 0) {
+               lostConn = 1;
+               return E_lostConn;
+       }
+       netBufSize += result;
+       if (netBufSize < netBufGoal)
+               return E_none;
+       memcpy(data, netBuf, sizeof(data));
+       type = ntoh2(data[0]);
+       size = ntoh2(data[1]);
+       netBufGoal = size;
+       if (netBufSize < netBufGoal)
+               return E_none;
+       netBufSize = 0;
+       netBufGoal = HEADER_SIZE;
+       event->u.net.type = type;
+       event->u.net.size = size - HEADER_SIZE;
+       event->u.net.data = netBuf + HEADER_SIZE;
+       if (type == NP_endConn) {
+               gotEndConn = 1;
+               return E_lostConn;
+       }
+       else if (type == NP_byeBye) {
+               lostConn = 1;
+               return E_lostConn;
+       }
+       return E_net;
+}
+
+ExtFunc void CheckNetConn(void)
+{
+}
+
+ExtFunc void SendPacket(NetPacketType type, int size, void *data)
+{
+       netint2 header[2];
+
+       header[0] = hton2(type);
+       header[1] = hton2(size + HEADER_SIZE);
+       if (MyWrite(sock, header, HEADER_SIZE) != HEADER_SIZE)
+               die("write");
+       if (size > 0 && data && MyWrite(sock, data, size) != size)
+               die("write");
+}
+
+ExtFunc void CloseNet(void)
+{
+       MyEvent event;
+
+       if (sock >= 0) {
+               if (!lostConn) {
+                       SendPacket(NP_endConn, 0, NULL);
+                       if (isServer) {
+                               while (!lostConn)
+                                       WaitMyEvent(&event, EM_net);
+                       }
+                       else {
+                               while (!gotEndConn)
+                                       WaitMyEvent(&event, EM_net);
+                               SendPacket(NP_byeBye, 0, NULL);
+                       }
+               }
+               close(sock);
+               sock = -1;
+       }
+       if (netGen.next)
+               RemoveEventGen(&netGen);
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/netris.h b/netris.h
new file mode 100644 (file)
index 0000000..5864cf7
--- /dev/null
+++ b/netris.h
@@ -0,0 +1,180 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: netris.h,v 1.27 1996/02/09 08:22:14 mhw Exp $
+ */
+
+#ifndef NETRIS_H
+#define NETRIS_H
+
+#include "config.h"
+#include <sys/time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <signal.h>
+
+#define ExtFunc                /* Marks functions that need prototypes */
+
+#ifdef NOEXT
+# define EXT
+# define IN(a) a
+#else
+# define EXT extern
+# define IN(a)
+#endif
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+#ifdef HAS_SIGPROCMASK
+typedef sigset_t MySigSet;
+#else
+typedef int MySigSet;
+#endif
+
+/*
+ * The following definitions are to ensure network compatibility even if
+ * the sizes of ints and shorts are different.  I'm not sure exactly how
+ * to deal with this problem, so I've added an abstraction layer.
+ */
+
+typedef short netint2;
+typedef long netint4;
+
+#define hton2(x) htons(x)
+#define hton4(x) htonl(x)
+
+#define ntoh2(x) ntohs(x)
+#define ntoh4(x) ntohl(x)
+
+#define DEFAULT_PORT 9284      /* Very arbitrary */
+
+#define DEFAULT_KEYS "jkl mspf^l"
+
+/* Protocol versions */
+#define MAJOR_VERSION          1       
+#define PROTOCOL_VERSION       3
+#define ROBOT_VERSION          1
+
+#define MAX_BOARD_WIDTH                32
+#define MAX_BOARD_HEIGHT       64
+#define MAX_SCREENS                    2
+
+#define DEFAULT_INTERVAL       300000  /* Step-down interval in microseconds */
+
+/* NP_startConn flags */
+#define SCF_usingRobot         000001
+#define SCF_fairRobot          000002
+#define SCF_setSeed                    000004
+
+/* Event masks */
+#define EM_alarm                       000001
+#define EM_key                         000002
+#define EM_net                         000004
+#define EM_robot                       000010
+#define EM_any                         000777
+
+typedef enum _GameType { GT_onePlayer, GT_classicTwo, GT_len } GameType;
+typedef enum _BlockTypeA { BT_none, BT_piece1, BT_wall, BT_len } BlockTypeA;
+typedef enum _Dir { D_down, D_right, D_up, D_left } Dir;
+typedef enum _Cmd { C_end, C_forw, C_back, C_left, C_right, C_plot } Cmd;
+typedef enum _FDType { FT_read, FT_write, FT_except, FT_len } FDType;
+typedef enum _MyEventType { E_none, E_alarm, E_key, E_net,
+                                                       E_lostConn, E_robot, E_lostRobot } MyEventType;
+typedef enum _NetPacketType { NP_endConn, NP_giveJunk, NP_newPiece,
+                                                       NP_down, NP_left, NP_right,
+                                                       NP_rotate, NP_drop, NP_clear,
+                                                       NP_insertJunk, NP_startConn,
+                                                       NP_userName, NP_pause, NP_version,
+                                                       NP_byeBye } NetPacketType;
+
+typedef signed char BlockType;
+
+typedef struct _MyEvent {
+       MyEventType type;
+       union {
+               char key;
+               struct {
+                       NetPacketType type;
+                       int size;
+                       void *data;
+               } net;
+               struct {
+                       int size;
+                       char *data;
+               } robot;
+       } u;
+} MyEvent;
+
+struct _EventGenRec;
+typedef MyEventType (*EventGenFunc)(struct _EventGenRec *gen, MyEvent *event);
+
+typedef struct _EventGenRec {
+       struct _EventGenRec *next;
+       int ready;
+       FDType fdType;
+       int fd;
+       EventGenFunc func;
+       int mask;
+} EventGenRec;
+
+typedef struct _Shape {
+       struct _Shape *rotateTo;
+       int initY, initX, mirrored;
+       Dir initDir;
+       BlockType type;
+       Cmd *cmds;
+} Shape;
+
+typedef struct _ShapeOption {
+       float weight;
+       Shape *shape;
+} ShapeOption;
+
+typedef int (*ShapeDrawFunc)(int scr, int y, int x,
+                                       BlockType type, void *data);
+
+EXT GameType game;
+EXT int boardHeight[MAX_SCREENS];
+EXT int boardVisible[MAX_SCREENS], boardWidth[MAX_SCREENS];
+EXT Shape *curShape[MAX_SCREENS];
+EXT int curY[MAX_SCREENS], curX[MAX_SCREENS];
+EXT char opponentName[16], opponentHost[256];
+EXT int standoutEnable;
+EXT int robotEnable, robotVersion, fairRobot;
+EXT int protocolVersion;
+
+EXT long initSeed;
+EXT long stepDownInterval, speed;
+
+EXT int myFlags, opponentFlags;
+
+EXT char scratch[1024];
+
+extern ShapeOption stdOptions[];
+extern char *version_string;
+
+#include "proto.h"
+
+#endif /* NETRIS_H */
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/robot.c b/robot.c
new file mode 100644 (file)
index 0000000..23875a3
--- /dev/null
+++ b/robot.c
@@ -0,0 +1,174 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: robot.c,v 1.8 1996/02/09 08:22:15 mhw Exp $
+ */
+
+#include "netris.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event);
+
+static EventGenRec robotGen =
+               { NULL, 0, FT_read, -1, RobotGenFunc, EM_robot };
+
+static int robotProcess;
+static FILE *toRobot;
+static int toRobotFd, fromRobotFd;
+
+static char robotBuf[128];
+static int robotBufSize, robotBufMsg;
+
+static int gotSigPipe;
+
+ExtFunc void InitRobot(char *robotProg)
+{
+       int to[2], from[2];
+       int status;
+       MyEvent event;
+
+       signal(SIGPIPE, CatchPipe);
+       AtExit(CloseRobot);
+       if (pipe(to) || pipe(from))
+               die("pipe");
+       robotProcess = fork();
+       if (robotProcess < 0)
+               die("fork");
+       if (robotProcess == 0) {
+               dup2(to[0], STDIN_FILENO);
+               dup2(from[1], STDOUT_FILENO);
+               close(to[0]);
+               close(to[1]);
+               close(from[0]);
+               close(from[1]);
+               execl("/bin/sh", "sh", "-c", robotProg, NULL);
+               die("execl failed");
+       }
+       close(to[0]);
+       close(from[1]);
+       toRobotFd = to[1];
+       robotGen.fd = fromRobotFd = from[0];
+       if (!(toRobot = fdopen(toRobotFd, "w")))
+               die("fdopen");
+       if ((status = fcntl(fromRobotFd, F_GETFL, 0)) < 0)
+               die("fcntl/F_GETFL");
+       status |= O_NONBLOCK;
+       if (fcntl(fromRobotFd, F_SETFL, status) < 0)
+               die("fcntl/F_SETFL");
+       AddEventGen(&robotGen);
+       RobotCmd(1, "Version %d\n", ROBOT_VERSION);
+       if (WaitMyEvent(&event, EM_robot) != E_robot)
+               fatal("Robot didn't start successfully");
+       if (1 > sscanf(event.u.robot.data, "Version %d", &robotVersion)
+                       || robotVersion < 1)
+               fatal("Invalid Version line from robot");
+       if (robotVersion > ROBOT_VERSION)
+               robotVersion = ROBOT_VERSION;
+}
+
+ExtFunc void CatchPipe(int sig)
+{
+       robotGen.ready = gotSigPipe = 1;
+}
+
+ExtFunc void RobotCmd(int flush, char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vfprintf(toRobot, fmt, args);
+       va_end(args);
+       if (flush)
+               fflush(toRobot);
+}
+
+ExtFunc void RobotTimeStamp(void)
+{
+       RobotCmd(1, "TimeStamp %.3f\n", CurTimeval() / 1.0e6);
+}
+
+ExtFunc void CloseRobot(void)
+{
+       RemoveEventGen(&robotGen);
+       if (robotProcess > 0)
+               RobotCmd(1, "Exit\n");
+       fclose(toRobot);
+       close(fromRobotFd);
+}
+
+static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event)
+{
+       static int more;
+       int result, i;
+       char *p;
+
+       if (gotSigPipe) {
+               gotSigPipe = 0;
+               robotGen.ready = more;
+               return E_lostRobot;
+       }
+       if (robotBufMsg > 0) {
+               /*
+                *      Grrrrrr!  SunOS 4.1 doesn't have memmove (or atexit)
+                *  I'm told some others have a broken memmove
+                *
+                *      memmove(robotBuf, robotBuf + robotBufMsg,
+                *                      robotBufSize - robotBufMsg);
+                */
+               for (i = robotBufMsg; i < robotBufSize; ++i)
+                       robotBuf[i - robotBufMsg] = robotBuf[i];
+
+               robotBufSize -= robotBufMsg;
+               robotBufMsg = 0;
+       }
+       if (more)
+               more = 0;
+       else {
+               do {
+                       result = read(fromRobotFd, robotBuf + robotBufSize,
+                                       sizeof(robotBuf) - robotBufSize);
+               } while (result < 0 && errno == EINTR);
+               if (result <= 0)
+                       return E_lostRobot;
+               robotBufSize += result;
+       }
+       if (!(p = memchr(robotBuf, '\n', robotBufSize))) {
+               if (robotBufSize >= sizeof(robotBuf))
+                       fatal("Line from robot is too long");
+               return E_none;
+       }
+       *p = 0;
+       robotBufMsg = p - robotBuf + 1;
+       robotGen.ready = more = (memchr(robotBuf + robotBufMsg, '\n',
+                       robotBufSize - robotBufMsg) != NULL);
+       event->u.robot.size = p - robotBuf;
+       event->u.robot.data = robotBuf;
+       return E_robot;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/robot_desc b/robot_desc
new file mode 100644 (file)
index 0000000..01c6970
--- /dev/null
@@ -0,0 +1,225 @@
+$Id: robot_desc,v 1.3 1996/02/09 08:22:16 mhw Exp $
+
+
+GENERAL PROTOCOL
+================
+When you pass the "-r <robot>" option to Netris, it launches
+"/bin/sh -c <robot>" as a subprocess and communicates to it via pipes.
+
+The robot reads events from Netris through stdin, and writes commands to
+stdout.  All exchanges are ASCII lines composed of tokens separated by
+spaces.  The first token in a line is the name of the command.
+
+The robot should ignore commands which it doesn't recognize.  The protocol
+may be extended by adding extra commands without incrementing the version
+number.  The version number will only be increased when the command cannot
+be safely ignored.
+
+Netris will also ignore commands which it doesn't recognize, for the same
+reason.
+
+
+INITIALIZATION
+==============
+The initial exchange between Netris and the robot is Version negotiation.
+Each sends a "Version <num>" line to the other, and the lowest version is
+used.  Currently, the robot protocol version is 1.
+
+Next, Netris sends "GameType <type>", there <type> is either OnePlayer
+or ClassicTwo.  There may be other games in the future.
+
+Then Netris sends "BoardSize <player> <height> <width>" for each player.
+<player> is 0 for the computer player and 1 for the opponent, if any.
+Other numbers may be used in the future.
+
+For each opponent, Netris sends "Opponent <player> <username> <host>".
+<host> is not necessarily a fully qualified host name, unfortunately.
+It might even be something like "localhost".  
+
+For each opponent, Netris may send 0 or more flags associated with the
+opponent.  For each flag which is true, Netris sends
+"OpponentFlag <player> <flag>".  Currently, the flags are "robot" and
+"fairRobot".
+
+Next, Netris sends "TickLength <seconds>", where <seconds> is the number
+of seconds between pieces stepping down.
+
+Finally, a "BeginGame" command is sent to the robot.
+
+
+NORMAL GAME (Netris --> robot)
+==============================
+Here's a list of commands sent from Netris to the robot, and a brief
+description of each one.
+
+Exit
+----
+This is always sent to the robot when the game is over, for any reason.
+The robot should exit immediately.
+
+NewPiece <num>
+--------------
+This command is never sent in "fair" robot mode.
+
+Everytime a new piece is created, this command is sent to the robot.
+<num> is a positive integer uniquely identifying the piece.  <num> should
+be sent as a parameter to each movement command (sent to Netris) in
+order to prevent accidental movement of the wrong piece.
+
+TimeStamp <seconds>
+-------------------
+This command may be sent to the robot at any time.  <seconds> is
+a floating point number of seconds since the beginning of the game.
+One is always sent immediately after "BeginGame" and "RowUpdate" commands.
+One will be sent at least every tick (see "TickLength") unless the game
+is paused.
+
+RowUpdate <player> <row> <col0> <col1> <col2> ... <coln>
+--------------------------------------------------------
+Whenever the screen changes, a group of these commands is sent to the
+robot followed by a "TimeStamp".  After a "RowUpdate" command, the robot's
+view of the board is incorrect until the "TimeStamp".
+
+<player> is 0 for the computer player and positive numbers for opponents.
+<row> is an integer from 0 to boardHeight-1.  0 is the bottom row.
+
+<col0> ... <coln> are integers separated by spaces, one for each column.
+"0" indicates an empty square.  Positive integers indicates blocks.
+Currently only "1" is used, but in the future there may be special kinds of
+blocks indicated by higher numbers.  Negative integers indicate part of
+the currently falling piece.  For each block, the absolute value of the
+number indicates the type of piece, and the sign indicates whether it is
+currently falling.
+
+If Netris is in "fair robot" (-F) mode, Netris will not highlight the
+falling piece with negative numbers.  In this case, all numbers will
+be non-negative.
+
+UserKey <key> <keyname>
+-----------------------
+Whenever the user presses a key, this command is sent to the robot.  The
+key is not automatically interpreted by Netris.
+
+<key> is the ascii value of the key, from 0 to 255.  <keyname> is one
+of the following strings: "Left", "Rotate", "Right", "Drop", "Down",
+"ToggleSpy", "Pause", or "?".  When possible, you should use <keyname>,
+since it will pay attention to keyboard remappings.
+
+For each possible <keyname> (other than "?"), there's an equivalent
+command recognized by Netris from the robot which performs the equivalent
+of the key.  Therefore, if you want give the user "manual" control, you
+can send Netris "<keyname> <piecenum>".
+
+Pause <pausedByMe> <pausedByThem>
+---------------------------------
+<pausedByMe> is 0 or 1, indicating whether the game is currently paused by
+our side.  <pausedByThem> is 0 or 1, indicating whether any player other
+than our side has paused the game.  If either is 1, the game is paused.
+
+You may actually receive this command even when none of the flags change.
+
+
+NORMAL GAME (robot --> Netris)
+==============================
+Here's a list of commands recognized by Netris from the robot:
+
+Left <num>
+Rotate <num>
+Right <num>
+Drop <num>
+Down <num>
+ToggleSpy <num>
+Pause <num>
+---------------
+These commands perform the equivalent of typing the corresponding command
+on the keyboard, if you weren't in robot mode.  
+
+<num> is checked against the current piece number (the value sent along
+with "NewPiece" commands).  If it doesn't match, the command is ignored.
+However, in "fair robot" mode, the <num> is ignored.
+
+These commands (except "Pause") are also ignored if the game is paused.
+
+Message <message>
+-----------------
+<message> is printed on the messages part of the display.  Messages too
+long to fit on the screen may be truncated.
+
+<message> may contain spaces and printable characters only.
+
+
+EXAMPLE
+=======
+Here's a portion of an example log generated by the sample robot.  The
+sample robot generates a log file in "log" if the "-l" is given to sr
+(eg "netris -r 'sr -l'").
+
+In this log file, every line is preceeded by two characters.  Lines
+sent from Netris to the robot are preceeded by two spaces "  ", and
+lines sent to Netris are preceeded by "> ".
+
+> Version 1
+  Version 1
+  GameType OnePlayer
+  BoardSize 0 20 10
+  TickLength 0.300
+  BeginGame
+  TimeStamp 0.009
+  NewPiece 1
+  RowUpdate 0 19 0 0 0 0 0 -1 -1 0 0 0
+  TimeStamp 0.010
+  RowUpdate 0 19 0 0 0 0 -1 -1 0 0 0 0
+  RowUpdate 0 18 0 0 0 0 0 -1 -1 0 0 0
+  TimeStamp 0.314
+> Message Goal 0 : ** :**  :    :    :
+> Left 1
+  TimeStamp 0.352
+  RowUpdate 0 19 0 0 0 -1 -1 0 0 0 0 0
+  RowUpdate 0 18 0 0 0 0 -1 -1 0 0 0 0
+  TimeStamp 0.362
+> Left 1
+  RowUpdate 0 19 0 0 -1 -1 0 0 0 0 0 0
+  RowUpdate 0 18 0 0 0 -1 -1 0 0 0 0 0
+  TimeStamp 0.375
+> Left 1
+  RowUpdate 0 19 0 -1 -1 0 0 0 0 0 0 0
+  RowUpdate 0 18 0 0 -1 -1 0 0 0 0 0 0
+  TimeStamp 0.387
+> Left 1
+  RowUpdate 0 19 -1 -1 0 0 0 0 0 0 0 0
+  RowUpdate 0 18 0 -1 -1 0 0 0 0 0 0 0
+  TimeStamp 0.399
+> Drop 1
+  RowUpdate 0 19 0 0 0 0 0 0 0 0 0 0
+  RowUpdate 0 18 0 0 0 0 0 0 0 0 0 0
+  RowUpdate 0 1 -1 -1 0 0 0 0 0 0 0 0
+  RowUpdate 0 0 0 -1 -1 0 0 0 0 0 0 0
+  TimeStamp 0.413
+  NewPiece 2
+  RowUpdate 0 19 0 0 0 0 0 -1 0 0 0 0
+  RowUpdate 0 1 1 1 0 0 0 0 0 0 0 0
+  RowUpdate 0 0 0 1 1 0 0 0 0 0 0 0
+  TimeStamp 0.715
+  RowUpdate 0 19 0 0 0 0 -1 -1 -1 0 0 0
+  RowUpdate 0 18 0 0 0 0 0 -1 0 0 0 0
+  TimeStamp 1.014
+> Message Goal 3 :*** : *  :    :    :
+> Rotate 2
+  TimeStamp 1.053
+  RowUpdate 0 19 0 0 0 0 0 -1 -1 0 0 0
+  RowUpdate 0 18 0 0 0 0 0 -1 0 0 0 0
+  TimeStamp 1.062
+  RowUpdate 0 19 0 0 0 0 0 -1 0 0 0 0
+  RowUpdate 0 18 0 0 0 0 0 -1 -1 0 0 0
+  RowUpdate 0 17 0 0 0 0 0 -1 0 0 0 0
+  TimeStamp 1.314
+> Rotate 2
+  RowUpdate 0 19 0 0 0 0 0 -1 0 0 0 0
+  RowUpdate 0 18 0 0 0 0 -1 -1 -1 0 0 0
+  RowUpdate 0 17 0 0 0 0 0 0 0 0 0 0
+  TimeStamp 1.326
+[...]
+  Exit
+
+
+# vi: tw=70 ai
diff --git a/shapes.c b/shapes.c
new file mode 100644 (file)
index 0000000..c62f69b
--- /dev/null
+++ b/shapes.c
@@ -0,0 +1,191 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: shapes.c,v 1.15 1996/02/09 08:22:17 mhw Exp $
+ */
+
+#include "netris.h"
+#include <stdlib.h>
+
+#define ShapeName(name, dir) \
+       shape_ ## name ## _ ## dir
+
+#define PreDecl(name, dir) \
+       static Shape ShapeName(name, dir)
+
+#define StdShape(name, cmdName, mirror, type, realDir, dir, nextDir) \
+       static Shape ShapeName(name, dir) = { &ShapeName(name, nextDir), \
+               0, 0, mirror, D_ ## realDir, type, cmds_ ## cmdName }
+
+#define FourWayDecl(name, cmdName, mirror, type) \
+       PreDecl(name, down); \
+       StdShape(name, cmdName, mirror, type, left, left, down); \
+       StdShape(name, cmdName, mirror, type, up, up, left); \
+       StdShape(name, cmdName, mirror, type, right, right, up); \
+       StdShape(name, cmdName, mirror, type, down, down, right)
+
+#define TwoWayDecl(name, cmdName, mirror, type) \
+       PreDecl(name, vert); \
+       StdShape(name, cmdName, mirror, type, right, horiz, vert); \
+       StdShape(name, cmdName, mirror, type, down, vert, horiz)
+
+static Cmd cmds_long[] = { C_back, C_plot, C_forw, C_plot, C_forw, C_plot,
+       C_forw, C_plot, C_end };
+TwoWayDecl(long, long, 0, BT_piece1);
+
+static Cmd cmds_square[] = { C_plot, C_forw, C_left, C_plot, C_forw, C_left,
+       C_plot, C_forw, C_left, C_plot, C_end };
+static Shape shape_square = { &shape_square, 0, 0, D_up, 0, BT_piece1,
+       cmds_square };
+
+static Cmd cmds_l[] = { C_right, C_back, C_plot, C_forw, C_plot, C_forw,
+       C_plot, C_left, C_forw, C_plot, C_end };
+FourWayDecl(l, l, 0, BT_piece1);
+FourWayDecl(l1, l, 1, BT_piece1);
+
+static Cmd cmds_t[] = { C_plot, C_forw, C_plot, C_back, C_right, C_forw,
+       C_plot, C_back, C_back, C_plot, C_end };
+FourWayDecl(t, t, 0, BT_piece1);
+
+static Cmd cmds_s[] = { C_back, C_plot, C_forw, C_plot, C_left, C_forw,
+       C_plot, C_right, C_forw, C_plot, C_end };
+TwoWayDecl(s, s, 0, BT_piece1);
+TwoWayDecl(s1, s, 1, BT_piece1);
+
+ShapeOption stdOptions[] = {
+       {1, &shape_long_horiz},
+       {1, &shape_square},
+       {1, &shape_l_down},
+       {1, &shape_l1_down},
+       {1, &shape_t_down},
+       {1, &shape_s_horiz},
+       {1, &shape_s1_horiz},
+       {0, NULL}};
+
+Shape *netMapping[] = {
+       &shape_long_horiz,
+       &shape_long_vert,
+       &shape_square,
+       &shape_l_down,
+       &shape_l_right,
+       &shape_l_up,
+       &shape_l_left,
+       &shape_l1_down,
+       &shape_l1_right,
+       &shape_l1_up,
+       &shape_l1_left,
+       &shape_t_down,
+       &shape_t_right,
+       &shape_t_up,
+       &shape_t_left,
+       &shape_s_horiz,
+       &shape_s_vert,
+       &shape_s1_horiz,
+       &shape_s1_vert,
+       NULL};
+
+ExtFunc void MoveInDir(Dir dir, int dist, int *y, int *x)
+{
+       switch (dir) {
+               case D_down:    *y -= dist; break;
+               case D_right:   *x += dist; break;
+               case D_up:              *y += dist; break;
+               case D_left:    *x -= dist; break;
+               default:
+                       assert(0);
+       }
+}
+
+ExtFunc Dir RotateDir(Dir dir, int delta)
+{
+       return 3 & (dir + delta);
+}
+
+ExtFunc int ShapeIterate(Shape *s, int scr, int y, int x, int falling,
+ExtFunc                                ShapeDrawFunc func, void *data)
+{
+       int i, mirror, result;
+       Dir dir;
+       BlockType type;
+
+       y += s->initY;
+       x += s->initX;
+       dir = s->initDir;
+       type = falling ? -s->type : s->type;
+       mirror = s->mirrored ? -1 : 1;
+       for (i = 0; s->cmds[i] != C_end; ++i)
+               switch (s->cmds[i]) {
+                       case C_forw:
+                               MoveInDir(dir, 1, &y, &x);
+                               break;
+                       case C_back:
+                               MoveInDir(dir, -1, &y, &x);
+                               break;
+                       case C_left:
+                               dir = RotateDir(dir, mirror);
+                               break;
+                       case C_right:
+                               dir = RotateDir(dir, -mirror);
+                               break;
+                       case C_plot:
+                               if ((result = func(scr, y, x, type, data)))
+                                       return result;
+                               break;
+                       default:
+                               assert(0);
+               }
+       return 0;
+}
+
+ExtFunc Shape *ChooseOption(ShapeOption *options)
+{
+       int i;
+       float total = 0, val;
+
+       for (i = 0; options[i].shape; ++i)
+               total += options[i].weight;
+       val = Random(0, 32767) / 32768.0 * total;
+       for (i = 0; options[i].shape; ++i) {
+               val -= options[i].weight;
+               if (val < 0)
+                       return options[i].shape;
+       }
+       return options[0].shape;
+}
+
+ExtFunc short ShapeToNetNum(Shape *shape)
+{
+       int num;
+
+       for (num = 0; netMapping[num]; ++num)
+               if (netMapping[num] == shape)
+                       return num;
+       assert(0);
+       return 0;
+}
+
+ExtFunc Shape *NetNumToShape(short num)
+{
+       assert(num >= 0 && num < sizeof(netMapping) / sizeof(netMapping[0]) - 1);
+       return netMapping[num];
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/sr.c b/sr.c
new file mode 100644 (file)
index 0000000..a5974fd
--- /dev/null
+++ b/sr.c
@@ -0,0 +1,474 @@
+/*
+ * sr -- A sample robot for Netris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: sr.c,v 1.10 1996/02/09 08:22:20 mhw Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+#include <limits.h>
+
+/* Both of these should be at least twice the actual max */
+#define MAX_BOARD_WIDTH                32
+#define MAX_BOARD_HEIGHT       64
+
+char b[1024];
+FILE *logFile;
+
+int twoPlayer;
+int boardHeight, boardWidth;
+int board[MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+int piece[4][4];
+int pieceLast[4][4];
+
+int board1[MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
+int piece1[4][4];
+int piece2[4][4];
+
+int pieceCount;                /* Serial number of current piece, for sending commands */
+int pieceVisible;      /* How many blocks of the current piece are visible */
+int pieceBottom, pieceLeft;    /* Position of bottom-left square */
+int pieceBottomLast, pieceLeftLast;
+
+/*
+ * 0 = Not decided yet
+ * 1 = decided
+ * 2 = move in progress
+ * 3 = drop in progress
+ */
+int pieceState;
+
+int leftDest;
+int pieceDest[4][4];
+
+int masterEnable = 1, dropEnable = 1;
+
+float curTime, moveTimeout;
+
+int min(int a, int b)
+{
+       return a < b ? a : b;
+}
+
+char *ReadLine(char *buf, int size)
+{
+       int len;
+
+       if (!fgets(buf, size, stdin))
+               return NULL;
+       len = strlen(buf);
+       if (len > 0 && buf[len-1] == '\n')
+               buf[len-1] = 0;
+       if (logFile)
+               fprintf(logFile, "  %s\n", buf);
+       return buf;
+}
+
+int WriteLine(char *fmt, ...)
+{
+       int result;
+       va_list args;
+
+       va_start(args, fmt);
+       result = vfprintf(stdout, fmt, args);
+       if (logFile) {
+               fprintf(logFile, "> ");
+               vfprintf(logFile, fmt, args);
+       }
+       va_end(args);
+       return result;
+}
+
+void FindPiece(void)
+{
+       int row, col;
+
+       pieceVisible = 0;
+       pieceBottom = MAX_BOARD_HEIGHT;
+       pieceLeft = MAX_BOARD_WIDTH;
+       for (row = boardHeight - 1; row >= 0; --row)
+               for (col = boardWidth - 1; col >= 0; --col)
+                       if (board[row][col] < 0) {
+                               pieceBottom = row;
+                               if (pieceLeft > col)
+                                       pieceLeft = col;
+                               pieceVisible++;
+                       }
+       for (row = 0; row < 4; ++row)
+               for (col = 0; col < 4; ++col)
+                       piece[row][col] = board[pieceBottom + row][pieceLeft + col] < 0;
+}
+
+void RotatePiece1(void)
+{
+       int row, col, height = 0;
+
+       for (row = 0; row < 4; ++row)
+               for (col = 0; col < 4; ++col)
+               {
+                       piece2[row][col] = piece1[row][col];
+                       piece1[row][col] = 0;
+                       if (piece2[row][col])
+                               height = row + 1;
+               }
+       for (row = 0; row < 4; ++row)
+               for (col = 0; col < height; ++col)
+                       piece1[row][col] = piece2[height - col - 1][row];
+}
+
+int PieceFits(int row, int col)
+{
+       int i, j;
+
+       if (row < 0)
+               return 0;
+       for (i = 0; i < 4; ++i)
+               for (j = 0; j < 4; ++j)
+                       if (piece1[i][j])
+                               if (col+j >= boardWidth || board[row+i][col+j] > 0)
+                                       return 0;
+       return 1;
+}
+
+int SimPlacement(int row, int col)
+{
+       int i, j;
+       int from, to, count;
+
+       for (i = 0; i < boardHeight; ++i)
+               for (j = 0; j < boardWidth; ++j) {
+                       board1[i][j] = board[i][j] > 0;
+                       if (i >= row && i < row+4 && j >= col && j < col+4)
+                               if (piece1[i - row][j - col])
+                                       board1[i][j] = 1;
+               }
+       for (from = to = 0; to < boardHeight; ++from) {
+               count = boardWidth;
+               for (j = 0; j < boardWidth; ++j)
+                       count -= board1[to][j] = board1[from][j];
+               to += (count > 0);
+       }
+       return from - to;
+}
+
+double BoardScore(int linesCleared, int pRow, int verbose)
+{
+       double score = 0;
+       double avgHeight2 = 0, avgHolesTimesDepth = 0;
+       int maxMidHeight = 0, maxHeight = 0;
+       int height[MAX_BOARD_WIDTH];
+       int holesTimesDepth[MAX_BOARD_WIDTH];
+       int holes[MAX_BOARD_WIDTH];
+       double hardFit[MAX_BOARD_HEIGHT];
+       int depend[MAX_BOARD_HEIGHT];
+       int cover[MAX_BOARD_WIDTH];
+       int row, col, count, i;
+       int deltaLeft, deltaRight;
+       double closeToTop, topShape = 0, fitProbs = 0, space = 0;
+       double maxHard;
+
+       for (col = 0; col < boardWidth; ++col) {
+               cover[col] = 0;
+               height[col] = 0;
+               for (row = 0; row < boardHeight; ++row)
+                       if (board1[row][col])
+                               height[col] = row + 1;
+               avgHeight2 += height[col] * height[col];
+               if (maxHeight < height[col])
+                       maxHeight = height[col];
+               if (col >= 2 && col < boardWidth - 2 && maxMidHeight < height[col])
+                       maxMidHeight = height[col];
+               holes[col] = 0;
+               holesTimesDepth[col] = 0;
+               for (row = 0; row < height[col]; ++row) {
+                       if (board1[row][col])
+                               holesTimesDepth[col] += holes[col];
+                       else
+                               holes[col]++;
+               }
+               avgHolesTimesDepth += holesTimesDepth[col];
+       }
+       avgHeight2 /= boardWidth;
+       avgHolesTimesDepth /= boardWidth;
+
+       /* Calculate dependencies */
+       for (row = maxHeight - 1; row >= 0; --row) {
+               depend[row] = 0;
+               for (col = 0; col < boardWidth; ++col) {
+                       if (board1[row][col])
+                               cover[col] |= 1 << row;
+                       else
+                               depend[row] |= cover[col];
+               }
+               for (i = row + 1; i < maxHeight; ++i)
+                       if (depend[row] & (1 << i))
+                               depend[row] |= depend[i];
+       }
+
+       /* Calculate hardness of fit */
+       for (row = maxHeight - 1; row >= 0; --row) {
+               hardFit[row] = 5;
+               count = 0;
+               for (col = 0; col < boardWidth; ++col) {
+                       if (board1[row][col]) {
+                               space += 0.5;
+                       }
+                       else if (!board1[row][col]) {
+                               count++;
+                               space += 1;
+                               hardFit[row]++;
+                               if (height[col] < row)
+                                       hardFit[row] += row - height[col];
+                               if (col > 0)
+                                       deltaLeft = height[col - 1] - row;
+                               else
+                                       deltaLeft = MAX_BOARD_HEIGHT;
+                               if (col < boardWidth - 1)
+                                       deltaRight = height[col + 1] - row;
+                               else
+                                       deltaRight = MAX_BOARD_HEIGHT;
+                               if (deltaLeft > 2 && deltaRight > 2)
+                                       hardFit[row] += 7;
+                               else if (deltaLeft > 2 || deltaRight > 2)
+                                       hardFit[row] += 2;
+                               else if (abs(deltaLeft) == 2 && abs(deltaRight) == 2)
+                                       hardFit[row] += 2;
+                               else if (abs(deltaLeft) == 2 || abs(deltaRight) == 2)
+                                       hardFit[row] += 3;
+                       }
+               }
+               maxHard = 0;
+               for (i = row + 1; i < row + 5 && i < maxHeight; ++i)
+                       if (depend[row] & (1 << i))
+                               if (maxHard < hardFit[i])
+                                       maxHard = hardFit[i];
+               fitProbs += maxHard * count;
+       }
+
+       /* Calculate score based on top shape */
+       for (col = 0; col < boardWidth; ++col) {
+               if (col > 0)
+                       deltaLeft = height[col - 1] - height[col];
+               else
+                       deltaLeft = MAX_BOARD_HEIGHT;
+               if (col < boardWidth - 1)
+                       deltaRight = height[col + 1] - height[col];
+               else
+                       deltaRight = MAX_BOARD_HEIGHT;
+               if (deltaLeft > 2 && deltaRight > 2)
+                       topShape += 15 + 15 * (min(deltaLeft, deltaRight) / 4);
+               else if (deltaLeft > 2 || deltaRight > 2)
+                       topShape += 2;
+               else if (abs(deltaLeft) == 2 && abs(deltaRight) == 2)
+                       topShape += 2;
+               else if (abs(deltaLeft) == 2 || abs(deltaRight) == 2)
+                       topShape += 3;
+       }
+
+       closeToTop = (pRow / (double)boardHeight);
+       closeToTop *= closeToTop;
+
+       closeToTop *= 200;
+       space /= 2;
+       score = space + closeToTop + topShape + fitProbs - linesCleared * 10;
+
+       if (verbose) {
+               WriteLine("Message space=%g, close=%g, shape=%g\n",
+                       space, closeToTop, topShape);
+               WriteLine("Message fitProbs=%g, cleared=%d\n",
+                       fitProbs, -linesCleared * 10);
+       }
+
+       return score;
+}
+
+void PrintGoal(void)
+{
+       char b[32];
+       int i, j, c;
+
+       c = 0;
+       for (i = 0; i < 4; ++i) {
+               b[c++] = ':';
+               for (j = 0; j < 4; ++j)
+                       b[c++] = pieceDest[i][j] ? '*' : ' ';
+       }
+       b[c++]=':';
+       b[c++]=0;
+       WriteLine("Message Goal %d %s\n", leftDest, b);
+}
+
+double MakeDecision(void)
+{
+       int row, col, rot;
+       int linesCleared;
+       int first = 1;
+       double minScore = 0, score;
+
+       memcpy(piece1, piece, sizeof(piece));
+       for (rot = 0; rot < 4; ++rot) {
+               RotatePiece1();
+               for (col = 0; col < boardWidth; ++col) {
+                       if (!PieceFits(pieceBottom, col))
+                               continue;
+                       for (row = pieceBottom; PieceFits(row-1, col); --row)
+                               ;
+                       linesCleared = SimPlacement(row, col);
+                       score = BoardScore(linesCleared, row, 0);
+                       if (first || minScore > score) {
+                               first = 0;
+                               minScore = score;
+                               memcpy(pieceDest, piece1, sizeof(piece));
+                               leftDest = col;
+                       }
+               }
+       }
+       PrintGoal();
+       return minScore;
+}
+
+double PeekScore(int verbose)
+{
+       int row, col, linesCleared;
+
+       memcpy(piece1, piece, sizeof(piece));
+       col = pieceLeft;
+       for (row = pieceBottom; PieceFits(row-1, col); --row)
+               ;
+       linesCleared = SimPlacement(row, col);
+       return BoardScore(linesCleared, row, verbose);
+}
+
+int main(int argc, char **argv)
+{
+       int ac;
+       char *av[32];
+
+       if (argc == 2 && !strcmp(argv[1], "-l")) {
+               logFile = fopen("log", "w");
+               if (!logFile) {
+                       perror("fopen log");
+                       exit(1);
+               }
+       }
+       setvbuf(stdout, NULL, _IOLBF, 0);
+       WriteLine("Version 1\n");
+       while(ReadLine(b, sizeof b)) {
+               av[0] = strtok(b, " ");
+               if (!av[0])
+                       continue;
+               ac = 1;
+               while ((av[ac] = strtok(NULL, " ")))
+                       ac++;
+               if (!strcmp(av[0], "Exit"))
+                       return 0;
+               else if (!strcmp(av[0], "NewPiece") && ac >= 2) {
+                       pieceCount = atoi(av[1]);
+                       pieceState = 0;
+               }
+               else if (!strcmp(av[0], "BoardSize") && ac >= 4) {
+                       if (atoi(av[1]) != 0)
+                               continue;
+                       boardHeight = atoi(av[2]);
+                       boardWidth = atoi(av[3]);
+               }
+               else if (!strcmp(av[0], "RowUpdate") && ac >= 3 + boardWidth) {
+                       int scr, row, col;
+
+                       scr = atoi(av[1]);
+                       if (scr != 0)
+                               continue;
+                       row = atoi(av[2]);
+                       for (col = 0; col < boardWidth; col++)
+                               board[row][col] = atoi(av[3 + col]);
+               }
+               else if (!strcmp(av[0], "UserKey") && ac >= 3) {
+                       char key;
+
+                       key = atoi(av[1]);
+                       switch (key) {
+                               case 'v':
+                               case 's':
+                                       FindPiece();
+                                       WriteLine("Message Score = %g\n", PeekScore(key == 'v'));
+                                       break;
+                               case 'e':
+                                       masterEnable = !masterEnable;
+                                       WriteLine("Message Enable = %d\n", masterEnable);
+                                       break;
+                               case 'd':
+                                       dropEnable = !dropEnable;
+                                       WriteLine("Message Drop Enable = %d\n", dropEnable);
+                                       break;
+                               default:
+                                       if (strcmp(av[2], "?"))
+                                               WriteLine("%s %d\n", av[2], pieceCount);
+                                       break;
+                       }
+               }
+               else if (!strcmp(av[0], "TimeStamp") && ac >= 2 && masterEnable) {
+                       curTime = atof(av[1]);
+                       FindPiece();
+                       if (pieceVisible < 4)
+                               continue;
+                       if (memcmp(piece, pieceLast, sizeof(piece)) ||
+                                       pieceLeft != pieceLeftLast) {
+                               if (pieceState == 2)
+                                       pieceState = 1;
+                               memcpy(pieceLast, piece, sizeof(piece));
+                               pieceLeftLast = pieceLeft;
+                       }
+                       if (pieceState == 0) {          /* Undecided */
+                               MakeDecision();
+                               pieceState = 1;
+                       }
+                       if (pieceState >= 2) {          /* Move or drop in progress */
+                               if (curTime >= moveTimeout)
+                                       pieceState = 1;
+                       }
+                       if (pieceState == 1) {          /* Decided */
+                               if (memcmp(piece, pieceDest, sizeof(piece))) {
+                                       WriteLine("Rotate %d\n", pieceCount);
+                                       pieceState = 2;
+                               }
+                               else if (pieceLeft != leftDest) {
+                                       if (pieceLeft < leftDest)
+                                               WriteLine("Right %d\n", pieceCount);
+                                       else
+                                               WriteLine("Left %d\n", pieceCount);
+                                       pieceState = 2;
+                               }
+                               else if (dropEnable) {
+                                       WriteLine("Drop %d\n", pieceCount);
+                                       pieceState = 3;
+                               }
+                               if (pieceState == 2)
+                                       moveTimeout = curTime + 0.5;
+                       }
+               }
+       }
+       return 0;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..98248ed
--- /dev/null
+++ b/util.c
@@ -0,0 +1,385 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * 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.
+ *
+ * $Id: util.c,v 1.27 1996/02/09 08:22:23 mhw Exp $
+ */
+
+#include "netris.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <errno.h>
+
+static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
+
+static EventGenRec alarmGen =
+               { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
+static EventGenRec *nextGen = &alarmGen;
+
+static myRandSeed = 1;
+
+static struct timeval baseTimeval;
+
+ExtFunc void InitUtil(void)
+{
+       if (initSeed)
+               SRandom(initSeed);
+       else
+               SRandom(time(0));
+       signal(SIGINT, CatchInt);
+       ResetBaseTime();
+}
+
+ExtFunc void ResetBaseTime(void)
+{
+       gettimeofday(&baseTimeval, NULL);
+}
+
+ExtFunc void AtExit(void (*handler)(void))
+{
+#ifdef HAS_ON_EXIT
+       on_exit((void *)handler, NULL);
+#else
+       atexit(handler);
+#endif
+}
+
+ExtFunc void Usage(void)
+{
+       fprintf(stderr,
+         "Netris version %s (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>\n"
+         "Usage: netris <options>\n"
+         "  -h         Print usage information\n"
+         "  -w         Wait for connection\n"
+         "  -c <host>  Initiate connection\n"
+         "  -p <port>  Set port number (default is %d)\n"
+         "  -k <keys>  Remap keys.  The argument is a prefix of the string\n"
+         "               containing the keys in order: left, rotate, right, drop,\n"
+         "               down-faster, toggle-spying, pause, faster, redraw.\n"
+         "               \"^\" prefixes controls.  (default is \"%s\")\n"
+         "  -i <sec>   Set the step-down interval, in seconds\n"
+         "  -r <robot> Execute <robot> (a command) as a robot controlling\n"
+         "               the game instead of the keyboard\n"
+         "  -F         Use fair robot interface\n"
+         "  -s <seed>  Start with given random seed\n"
+         "  -D         Drops go into drop mode\n"
+         "               This means that sliding off a cliff after a drop causes\n"
+         "               another drop automatically\n"
+         "  -S         Disable standout mode (inverse/bold) for slow terminals\n"
+         "  -H         Show distribution and warranty information\n"
+         "  -R         Show rules\n",
+         version_string, DEFAULT_PORT, DEFAULT_KEYS);
+}
+
+ExtFunc void DistInfo(void)
+{
+       fprintf(stderr,
+         "Netris version %s (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>\n"
+         "\n"
+         "This program 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"
+         "\n"
+         "This program 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"
+         "\n"
+         "You should have received a copy of the GNU General Public License\n"
+         "along with this program; if not, write to the Free Software\n"
+         "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n",
+         version_string);
+}
+
+ExtFunc void Rules(void)
+{
+       fprintf(stderr,
+         "Netris version %s rules\n"
+         "\n"
+         "Two player mode\n"
+         "---------------\n"
+         "It's just like normal T*tris except that when you clear more than\n"
+         "one row with a single piece, the other player's board is moved up\n"
+         "and junk rows are added to the bottom.  If you clear 2, 3 or 4\n"
+         "rows, 1, 2 or 4 junk rows are added to your opponent's board,\n"
+         "respectively.  The junk rows have exactly one empty column.\n"
+         "For each group of junk rows given, the empty columns will line\n"
+         "up.  This is intentional.\n"
+         "\n"
+         "The longest surviving player wins the game.\n"
+         "\n"
+         "One player mode\n"
+         "---------------\n"
+         "This mode is currently very boring, because there's no scoring\n"
+         "and it never gets any faster.  This will be rectified at some point.\n"
+         "I'm not very motivated to do it right now because I'm sick of one\n"
+         "player T*tris.  For now, use the \"f\" key (by default) to make the\n"
+         "game go faster.  Speedups cannot be reversed for the remainder of\n"
+         "the game.\n",
+         version_string);
+}
+
+/*
+ * My really crappy random number generator follows
+ * Should be more than sufficient for our purposes though
+ */
+ExtFunc void SRandom(int seed)
+{
+       initSeed = seed;
+       myRandSeed = seed % 31751 + 1;
+}
+
+ExtFunc int Random(int min, int max1)
+{
+       myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
+       return myRandSeed % (max1 - min) + min;
+}
+
+ExtFunc int MyRead(int fd, void *data, int len)
+{
+       int result, left;
+
+       left = len;
+       while (left > 0) {
+               result = read(fd, data, left);
+               if (result > 0) {
+                       data = ((char *)data) + result;
+                       left -= result;
+               }
+               else if (errno != EINTR)
+                       return result;
+       }
+       return len;
+}
+
+ExtFunc int MyWrite(int fd, void *data, int len)
+{
+       int result, left;
+
+       left = len;
+       while (left > 0) {
+               result = write(fd, data, left);
+               if (result > 0) {
+                       data = ((char *)data) + result;
+                       left -= result;
+               }
+               else if (errno != EINTR)
+                       return result;
+       }
+       return len;
+}
+
+ExtFunc void NormalizeTime(struct timeval *tv)
+{
+       tv->tv_sec += tv->tv_usec / 1000000;
+       tv->tv_usec %= 1000000;
+       if (tv->tv_usec < 0) {
+               tv->tv_usec += 1000000;
+               --tv->tv_sec;
+       }
+}
+
+ExtFunc void CatchInt(int sig)
+{
+       exit(0);
+}
+
+ExtFunc void CatchAlarm(int sig)
+{
+       alarmGen.ready = 1;
+       signal(SIGALRM, CatchAlarm);
+}
+
+static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
+{
+       return E_alarm;
+}
+
+ExtFunc long CurTimeval(void)
+{
+       struct timeval tv;
+
+       gettimeofday(&tv, NULL);
+       tv.tv_sec -= baseTimeval.tv_sec;
+       tv.tv_usec -= baseTimeval.tv_usec;
+       return GetTimeval(&tv);
+}
+
+ExtFunc void SetTimeval(struct timeval *tv, long usec)
+{
+       tv->tv_sec = usec / 1000000;
+       tv->tv_usec = usec % 1000000;
+}
+
+ExtFunc long GetTimeval(struct timeval *tv)
+{
+       return tv->tv_sec * 1000000 + tv->tv_usec;
+}
+
+static long SetITimer1(long interval, long value)
+{
+       struct itimerval it, old;
+
+       SetTimeval(&it.it_interval, interval);
+       SetTimeval(&it.it_value, value);
+       if (setitimer(ITIMER_REAL, &it, &old) < 0)
+               die("setitimer");
+       signal(SIGALRM, CatchAlarm);
+       return GetTimeval(&old.it_value);
+}
+
+ExtFunc long SetITimer(long interval, long value)
+{
+       long old;
+
+       old = SetITimer1(0, 0);
+       alarmGen.ready = 0;
+       SetITimer1(interval, value);
+       return old;
+}
+
+ExtFunc volatile void die(char *msg)
+{
+       perror(msg);
+       exit(1);
+}
+
+ExtFunc volatile void fatal(char *msg)
+{
+       fprintf(stderr, "%s\n", msg);
+       exit(1);
+}
+
+ExtFunc void BlockSignals(MySigSet *saved, ...)
+{
+       MySigSet set;
+       va_list args;
+       int sig;
+
+       va_start(args, saved);
+#ifdef HAS_SIGPROCMASK
+       sigemptyset(&set);
+#else
+       set = 0;
+#endif
+       while ((sig = va_arg(args, int))) {
+#ifdef HAS_SIGPROCMASK
+               sigaddset(&set, sig);
+#else
+               sig |= sigmask(sig);
+#endif
+       }
+#ifdef HAS_SIGPROCMASK
+       sigprocmask(SIG_BLOCK, &set, saved);
+#else
+       *saved = sigblock(set);
+#endif
+       va_end(args);
+}
+
+ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
+{
+#ifdef HAS_SIGPROCMASK
+       sigprocmask(SIG_SETMASK, set, saved);
+#else
+       if (saved)
+               *saved = sigsetmask(*set);
+       else
+               sigsetmask(*set);
+#endif
+}
+
+ExtFunc void AddEventGen(EventGenRec *gen)
+{
+       assert(gen->next == NULL);
+       gen->next = nextGen->next;
+       nextGen->next = gen;
+}
+
+ExtFunc void RemoveEventGen(EventGenRec *gen)
+{
+       assert(gen->next != NULL);
+       while (nextGen->next != gen)
+               nextGen = nextGen->next;
+       nextGen->next = gen->next;
+       gen->next = NULL;
+}
+
+ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
+{
+       int i, retry = 0;
+       fd_set fds[FT_len];
+       EventGenRec *gen;
+       int result, anyReady, anySet;
+       struct timeval tv;
+
+       /* XXX In certain circumstances, this routine does polling */
+       for (;;) {
+               for (i = 0; i < FT_len; ++i)
+                       FD_ZERO(&fds[i]);
+               anyReady = anySet = 0;
+               gen = nextGen;
+               do {
+                       if (gen->mask & mask) {
+                               if (gen->ready)
+                                       anyReady = 1;
+                               if (gen->fd >= 0) {
+                                       FD_SET(gen->fd, &fds[gen->fdType]);
+                                       anySet = 1;
+                               }
+                       }
+                       gen = gen->next;
+               } while (gen != nextGen);
+               if (anySet) {
+                       tv.tv_sec = 0;
+                       tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
+                       result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
+                                       &fds[FT_except], anyReady ? &tv : NULL);
+               }
+               else {
+                       if (retry && !anyReady)
+                               sleep(1);
+                       result = 0;
+               }
+               gen = nextGen;
+               do {
+                       if ((gen->mask & mask)
+                                       && (gen->ready || (result > 0 && gen->fd >= 0
+                                               && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
+                               gen->ready = 0;
+                               event->type = gen->func(gen, event);
+                               if (event->type != E_none) {
+                                       nextGen = gen->next;
+                                       return event->type;
+                               }
+                       }
+                       gen = gen->next;
+               } while (gen != nextGen);
+               retry = 1;
+       }
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */