--- /dev/null
+ 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.
--- /dev/null
+:
+#
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+$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
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */
--- /dev/null
+/*
+ * 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
+ */