pkg://tt-meista-1.0-1.src.rpm:33155/tt-meista.tar.gz
info downloads
tt-meista/ 40755 764 144 0 6451251053 11570 5 ustar thomas users tt-meista/README 100644 764 144 16312 5200054415 12562 0 ustar thomas users Tetris for Terminals - "tt" - Written by Mike Taylor
====================================================
(1) Specification
==================
This is a game written in C for Berkeley UNIX machines. It
was written on a Sun4 and has been extensively played and
tested on both this machine and a Sun3, both running SunOS
4.0.1, a Berkeley 4.3bsd-derived UNIX, but it should port with
minimum difficulties to any Berkeley UNIX. One potential
difficulty to look out for is incompatibility of the high-
score file between different architectures, (eg. sun3 and
sun4). I've done all I can to make it work, but there are no
guarantees, and you might be better compiling multiple-
architecture "tt"s with different high-score tables.
(2) Compilation
================
The Makefile as included should be pretty much applicable
anywhere. Simply edit the file "tt.h" to give the pathname of
the file you want to use for the high-score table, and unless
you are using the LOCKF #definition (also in tt.h), the file
you want to use as a lock for this table (which should be in a
publicly writeable directory). Then type "make". The program
will be compiled, producing a binary called "tt". This can
then be moved to a bin directory if required.
(3) Acknowledgements
====================
The game Tetris was apparently designed by "a Russian
Researcher". Sadly, history, or at least the version of it
that I heard, does not record his name, and it seems that the
poor frog-head not only made no money from his brilliant idea,
but also didn't get any fame. 'S'sad. Still, I credit him
with the original idea, whoever he may be.
The inspiration for this program came from two recently-posted
versions of Tetris, both of which run on Sun workstations
only. There are a few of these around here, but many people
have no access to them. I wrote this version so that everyone
could play. It's the communist in me :-)
The program design, planning (hah!), coding, and so on for
this version was all done entirely by me, (Mike Taylor), and
very smug I am about it, too. The only exception to this is
that the game-levels were accidentally co-invented by James
"root@weed" Beckett, by pressing the space-bar too many times.
Play-testing, which has been extensive even now, less than a
week after I started writing, has been by many people, but
special mention goes to Paul Metcalfe, Kenton Oatley, Harvey
"Max" Thomson and Paul "Freddy" Capper. No mention *at all*,
not even this one, goes to Mike "Sunny" Lessacher, who claims
not to like Tetris. ("Not like Tetris? What does that
mean?" :-)
All documentation is also by me.
(4) The Legal Position
======================
Look, I'm sorry, I *know* this bit is dull, but it has to be
done, and it saves time and trouble for everyone if we just
get on with it. here we go:
The program "tt", its visual appearance, its code, its
documentation, etc., are the intellectual property of
Mike Taylor. The program may be freely distributed,
copied, modified, re-posted or whatever PROVIDED that
the authorship and ownership remains clear, and that
no-one makes any money from it without me knowing (and
taking a hefty cut!) Whoops, that's spoilt the
official feel to it. Never mind, you get the idea.
Do what you want provided it isn't sneaky.
(5) The Game
============
Tetris is one of those simple-but-compulsive games that you
persistently find yourself wanting to play "just once more".
I know, I know what you're thinking, "We've all heard that
before!", but it just happens to be true on this occasion.
The object of the game is simply to prevent a stack of blocks
from building up to the top of the play-area, (to the left of
the screen). You do this by rotating and shifting the blocks
as they fall, in such a way as to make them fit together as
well as possible at the bottom. If you manage to complete a
whole line, from right to left, then that row will disappear,
and all the rows above will fall down into its place. it is
possible to get more than one row at once, and it is a
wonderful feeling to get four at once!
There are five types of block, (seven if you count mirror
images), each of which is made up of four squares stuck
together, (hence "Tetris", I assume). These pieces score
different numbers of points based on how difficult or
otherwise it is to fit them into the place required. The
pieces are:
###### "T-shape" 1 point.
##
<><><><> "Long one" 2 points.
() {}
() {} "L-shapes" 3 points.
()() {}{}
[][] "Square" 4 points.
[][]
%% @@ "S-shapes" 5 points.
%%%% @@@@
%% @@
Once a piece has been positioned where you want it, it can be
dropped into place immediately. There are no extra points for
this, 'cos I think it would spoil the purity of the scoring
system; it just speeds up the game.
Hehehe, that reminds me, as the game progresses, it very very
gradually speeds up, until, at scores of around 3000, it gets
very difficult indeed ... I mean *very* difficult!
The keys that control the pieces, and other special keys
(those used to Quit or Pause the game, or Refresh the screen)
are shown on the screen. They can be re-defined (except the
Refresh key), as explained in the manual page.
(6) Basic Hints
================
Obviously, the aim is to keep the stack low, so at all times,
you should be on the lookout for ways to fit the currently-
falling piece onto those that have already fallen in such a
way as to complete rows. Removing rows is the key to success.
However, if a lower section of the screen gets hopelessly
hole-ridden, it is sometimes best to forget about it, and
concentrate on building complete rows higher up, as this can
often be the best way to make the holes lower down become
available again. A good player can take over from a game 2/3
full of badly-packed pieces, and eventually wrestle it right
back down to ground level again.
Finally, there is a tendency among beginners to leave long,
thin gaps down the sides of the screen, praying for a "Long
one" to slide down it -- it is, of course, at precisely these
times that a succession of S-shapes, all of the same
handedness, comes pouring down the screen. The moral is
two-fold: (1) Beware of allowing such a situation to build up
in the first place, it often isn't necessary, and (2), learn
how to remove layers from higher up the screen with non-long
pieces, so your screen doesn't fill up while you wait for that
magical "Long one".
(7) What to do if you don't like it
====================================
Email me at the following address: mirk@uk.ac.warwick.cs
In fact, email me if you *do* like it. Email me with
bouquets, brickbats, bugs, baboons, bachelors, blueberries,
boathouses, er ... um, sorry, I seem to have got a bit carried
away. Anyway, the point is, let me know what you think of
"tt", and it might just influence the next version. Also, I
am pretty good at replying to mail!
That's it -- have fun!
______________________________________________________________________________
Mike Taylor - {Christ,M{athemat,us}ic}ian ... Email to: mirk@uk.ac.warwick.cs
"Imagine the universe perfect and whole and beautiful. Then be sure
of one thing: God has imagined it quite a bit better than you"
tt-meista/MANIFEST 100644 764 144 1423 6451215407 13021 0 ustar thomas users File Name | Description
----------------+---------------------------------------------------
MANIFEST | This file -- a shipping list.
Makefile | See make(1) -- this file directs compilation.
README | General notes and instructions.
game.c | Module containing the main game loop.
game.h | Header file for above.
pieces.c | Module containing definitions of pieces.
pieces.h | Header file for above.
screen.c | Module containing all curses(3x)-based bits.
screen.h | Header file for above.
tt.6 | Manual entry (install in /usr/man/man6)
tt.c | Module contaning main function and initialisation.
tt.h | Header file for above.
ttscores | Sample high-score table (might not work).
utils.c | Module containing re-usable general utilities.
utils.h | Header file for above.
tt-meista/game.c 100644 764 144 21773 6451250736 13003 0 ustar thomas users /***************************************************************************\
|* *|
|* game.c: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This is the module that *|
|* actually plays the game, (ie. moves things down the *|
|* screen, select(2)s on the keyboard, and so on) *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <errno.h>
/* Here's what is need for this under Linux */
void l_usleep (unsigned long usec)
{
struct timeval timeout;
timeout.tv_sec = usec / 1000000;
timeout.tv_usec = usec - 1000000 * timeout.tv_sec;
select (1, NULL, NULL, NULL, &timeout);
}
#include "tt.h"
#include "game.h"
#include "screen.h"
#include "pieces.h"
#include "utils.h"
/*-------------------------------------------------------------------------*/
extern long int random ();
/*-------------------------------------------------------------------------*/
int left_key = LEFT_KEY; /* Move piece left */
int right_key = RIGHT_KEY; /* Move piece right */
int rotate_key = ROTATE_KEY; /* Rotate piece anticlockwise */
int drop_key = DROP_KEY; /* Drop piece to bottom of screen */
int susp_key = SUSP_KEY; /* Suspend. I'm sorry if its confusing */
int quit_key = QUIT_KEY; /* Quit. I'm sorry if its confusing */
int cheat_key = CHEAT_KEY; /* Do whatever I eventaully make it do */
/***************************************************************************\
|* *|
|* Oh good grief, what on earth is the point of putting a huge, wobbly *|
|* box-comment in front of this titchy little self-explanatory function? *|
|* The name tells the whole story, it's perfectly strightforward, it's *|
|* not a proposition from Witgenstein. I wouldn't bother commenting it *|
|* at all if it wasn't for the fact that I know I'd feel guilty in the *|
|* morning. I mean, be fair, you don't really want a program where all *|
|* the functions *except one* have box-comments explaining them, do you? *|
|* Ah well, here we go for completion's sake: *|
|* *|
|* The function clear_board() takes no parameters and returns no value. *|
|* It clears the board. The end. *|
|* *|
\***************************************************************************/
void clear_board ()
{
int i, j;
for (i = 0; i < GAME_DEPTH+4; i++)
for (j = 0; j < GAME_WIDTH; j++)
board[i][j] = PI_EMPTY;
}
/***************************************************************************\
|* *|
|* The function play_game is the main loop in which the game of Tetris *|
|* is implemented. It takes no parameters, and returns no value. The *|
|* time at which it returns no value is the end of a game. The main *|
|* loop-component is a select(2) which polls the keyboard in real-time. *|
|* If you use a non-Berkeley UNIX(tm), you're well cheesed. *|
|* *|
|* Actually, I have to admit I'm not proud of this one. It must be one *|
|* of the messiest functions I've written in years, in terms of nested *|
|* loops with ad-hoc exit conditions, re-used variables, general *|
|* obfuscation and so on. I wanna make it quite clear that I make no *|
|* apologies for my use of "goto", which remains a highly desirable *|
|* language feature, and is still the most elegant way of coding many *|
|* things, but I gotta admit, overall this one is bit of a chicken. *|
|* *|
\***************************************************************************/
void play_game ()
{
int i; /* Position of origin down board */
int j; /* Position of origin across board */
int k; /* Loop variable when i,j are invariant */
int piece_no; /* Type of piece currently falling */
int orient; /* Which way it is facing */
int pause_flag = 0; /* We won't pause unless told to */
int presses_left; /* Futher moves possible this drop */
int free_left = game_level; /* Number of pieces to drop at start */
fd_set read_fds; /* Select must look at stdin */
long int total_time = 200000; /* Allow 1/4 second before falling */
struct timeval timeout; /* Time before piece drops in select(2) */
int ch; /* Keystroke (command) */
score = no_levels = no_pieces = 0;
update_scores ();
clear_board ();
while (1) {
piece_no = (int) (random () % NO_PIECES);
orient = (int) (random () % NO_ORIENTS);
i = -2; /* Start falling from off-screen */
if (free_left > 0) /* If we have random starting-pieces */
j = 1 + (int) (random () % (GAME_WIDTH-2));
else /* places them randomly, otherwise */
j = GAME_WIDTH/2; /* put the piece in the middle */
if (!can_place (piece_no, orient, i, j)) {
for (k = 0; k < 9; k++) { /* Crude but (hopefully) effective */
draw_piece (piece_no, orient, i, j, PD_ERASE);
myrefresh (); l_usleep (80000);
draw_piece (piece_no, orient, i, j, PD_DRAW);
myrefresh (); l_usleep (80000);
}
break; /* End of game - piece won't fit */
}
if (free_left != 0) { /* If there are pieces to be dropped, */
if (free_left > 0) /* And the number is positiive, */
free_left--; /* Then decrement it, otherwise */
else /* increment it, in any case, bring */
free_left++; /* it closer to zero if it gets to zero */
if (free_left == 0) /* set a flag so that the game will */
pause_flag = 1; /* pause. Then go to the bit of code */
goto DROP_PIECE; /* that drops it. */
}
while (1) {
presses_left = NO_MOVES;
draw_piece (piece_no, orient, i, j, PD_DRAW);
update_scores ();
myrefresh ();
while (1) {
FD_ZERO (&read_fds);
FD_SET (0, &read_fds);
timeout.tv_sec = 0;
timeout.tv_usec = total_time;
switch (select (((presses_left > 0) && (i >= 0)), &read_fds,
(fd_set*) NULL, (fd_set*) NULL, &timeout)) {
case -1:
if (errno != EINTR)
die (LE_SELECT, "select(2) failed in play_game()");
/* Otherwise fall through, goto TIMEOUT */
case 0:
goto TIMEOUT;
default:
ch = getch();
if (ch == REFRESH_KEY)
hoopy_refresh ();
if ((ch != left_key) && (ch != right_key) && (ch != rotate_key) &&
(ch != drop_key) && (ch != quit_key) && (ch != susp_key) &&
(ch != cheat_key))
break;
presses_left--;
if (ch == left_key) {
if (can_place (piece_no, orient, i, j-1)) {
draw_piece (piece_no, orient, i, j, PD_ERASE);
j--;
draw_piece (piece_no, orient, i, j, PD_DRAW);
myrefresh ();
}
}
else if (ch == right_key) {
if (can_place (piece_no, orient, i, j+1)) {
draw_piece (piece_no, orient, i, j, PD_ERASE);
j++;
draw_piece (piece_no, orient, i, j, PD_DRAW);
myrefresh ();
}
}
else if (ch == rotate_key) {
int new_or = ((rotate_backwards == 0) ? ((orient+1)%NO_ORIENTS) :
((orient-1)%NO_ORIENTS+NO_ORIENTS*(orient == 0)));
if (can_place (piece_no, new_or, i, j)) {
draw_piece (piece_no, orient, i, j, PD_ERASE);
orient = new_or;
draw_piece (piece_no, orient, i, j, PD_DRAW);
myrefresh ();
}
}
else if (ch == drop_key) {
DROP_PIECE:
while (can_place (piece_no, orient, i+1, j)) {
draw_piece (piece_no, orient, i, j, PD_ERASE);
i++;
draw_piece (piece_no, orient, i, j, PD_DRAW);
myrefresh ();
}
goto TIMEOUT;
}
else if (ch == quit_key)
return;
else if (ch == cheat_key) {
print_msg ("CHEAT!");
total_time = 0L;
}
else if (ch == susp_key) {
print_msg ("Paused");
(void) read (0, &ch, 1);
print_msg ("");
}
break;
}
}
TIMEOUT:
if (!can_place (piece_no, orient, i+1, j)) {
place_piece (piece_no, orient, i, j);
score += pieces[piece_no].points;
no_pieces++;
update_scores ();
myrefresh ();
for (i = 0; i < GAME_DEPTH; i++) {
for (j = 0; j < GAME_WIDTH; j++)
if (board[i+4][j] == PI_EMPTY)
break;
if (j == GAME_WIDTH) {
no_levels++;
score += 10;
update_scores ();
for (k = i; k > 0; k--)
for (j = 0; j < GAME_WIDTH; j++)
board[k+4][j] = board[k+3][j];
for (j = 0; j < GAME_WIDTH; j++)
board[4][j] = PI_EMPTY;
draw_board ();
myrefresh ();
i--; /* Check the new row i */
}
}
if (pause_flag) { /* If we are pausing after this drop ... */
pause_flag = 0; /* Ensure we don't do so next time. */
flush_keyboard ();
print_msg ("Continue");
(void) read (0, &ch, 1);
print_msg ("");
}
break; /* End of fall - piece has hit floor */
}
draw_piece (piece_no, orient, i, j, PD_ERASE);
i++;
}
myrefresh ();
if (total_time != 0)
total_time -= 100;
}
}
/*-------------------------------------------------------------------------*/
tt-meista/pieces.c 100644 764 144 4202 6451250722 13301 0 ustar thomas users /***************************************************************************\
|* *|
|* pieces.c: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module contains the *|
|* definitions of the pieces. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#include "tt.h"
#include "pieces.h"
/*-------------------------------------------------------------------------*/
struct piece pieces[NO_PIECES] = {
{ "[]", 4, /* Square piece */
{
{{0,0}, {0,1}, {1,0}, {1,1}},
{{0,0}, {0,1}, {1,0}, {1,1}},
{{0,0}, {0,1}, {1,0}, {1,1}},
{{0,0}, {0,1}, {1,0}, {1,1}}
}
},
{ "<>", 2, /* Long piece */
{
{{0,0}, {1,0}, {2,0}, {3,0}},
{{1,-1}, {1,0}, {1,1}, {1,2}},
{{0,0}, {1,0}, {2,0}, {3,0}},
{{1,-1}, {1,0}, {1,1}, {1,2}}
}
},
{ "()", 3, /* L-shaped piece */
{
{{0,0}, {1,0}, {2,0}, {2,1}},
{{0,1}, {1,-1}, {1,0}, {1,1}},
{{0,-1}, {0,0}, {1,0}, {2,0}},
{{1,-1}, {1,0}, {1,1}, {2,-1}}
}
},
{ "{}", 3, /* Backwards L-shaped piece */
{
{{0,0}, {1,0}, {2,-1}, {2,0}},
{{1,-1}, {1,0}, {1,1}, {2,1}},
{{0,0}, {0,1}, {1,0}, {2,0}},
{{0,-1}, {1,-1}, {1,0}, {1,1}}
}
},
{ "##", 1, /* T-shaped piece */
{
{{1,-1}, {1,0}, {1,1}, {2,0}},
{{0,0}, {1,0}, {1,1}, {2,0}},
{{0,0}, {1,-1}, {1,0}, {1,1}},
{{0,0}, {1,-1}, {1,0}, {2,0}}
}
},
{ "%%", 5, /* S-shaped piece */
{
{{0,0}, {0,1}, {1,-1}, {1,0}},
{{0,-1}, {1,-1}, {1,0}, {2,0}},
{{0,0}, {0,1}, {1,-1}, {1,0}},
{{0,-1}, {1,-1}, {1,0}, {2,0}}
}
},
{ "@@", 5, /* Backwards S-shaped piece */
{
{{0,-1}, {0,0}, {1,0}, {1,1}},
{{0,0}, {1,-1}, {1,0}, {2,-1}},
{{0,-1}, {0,0}, {1,0}, {1,1}},
{{0,0}, {1,-1}, {1,0}, {2,-1}}
}
},
};
/*-------------------------------------------------------------------------*/
tt-meista/screen.c 100644 764 144 33426 6451250653 13345 0 ustar thomas users /***************************************************************************\
|* *|
|* screen.c: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module handles all *|
|* the icky curses(3x) bits. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#include <curses.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include "screen.h"
#include "tt.h"
#include "pieces.h"
#include "utils.h"
#include "game.h"
char intro1[] = "\n"
" ________ ________ ________ _______ ________ _______\n"
" %%%%%%%% %%%%%%%% %%%%%%%% %%%%%%%_ %%%%%%%% _%%%%%%%\n"
" %% %% %% %% %% %% %% \n"
" %% %%___ %% %%____%% %% %%_____ \n"
" %% %%%%% %% %%%%%%_ %% %%%%%%_\n"
" %% %% %% %% %%_ %% %%\n"
" %% %%______ %% %% %% ___%%___ ______%%\n"
" %% %%%%%%%% %% %% %% %%%%%%%% %%%%%%% \n";
char intro2[] =
" // //////// // // // //////// /////// \n"
" // // // // // // // \n"
" // ///// // ////// // ////// \n"
" // // // // // // // \n"
" //////// //////// //////// /////// //////// /////// \n\n\n";
char intro3[] =
" Written by Mike Taylor <mirk@uk.ac.warwick.cs> \n"
" Modified by Thomas Meisterburg <tmeisterburg@pader-online.de> ";
/***************************************************************************\
|* *|
|* show_intro(): shows an intro-screen :-) *|
|* *|
\***************************************************************************/
void show_intro(void) {
attrset(A_BOLD | COLOR_PAIR(COLOR_BLUE));
addstr(intro1);
attrset(COLOR_PAIR(COLOR_BLUE));
addstr(intro2);
attrset(COLOR_PAIR(COLOR_RED));
addstr(intro3);
attrset(A_BLINK);
mvaddstr(24, 0, "Hit a key to start...");
attrset(A_NORMAL);
getch();
clear();
}
/***************************************************************************\
|* *|
|* The function myrefresh() calls the curses(3x) function refresh() *|
|* after first moving the cursor out of harm's way at the bottom of *|
|* the screen. *|
|* *|
\***************************************************************************/
void myrefresh ()
{
int x;
if ((x = screen_depth-2) < GAME_DEPTH+1)
x = GAME_DEPTH+1;
move (x, 0);
refresh ();
}
/***************************************************************************\
|* *|
|* The function hoopy_refresh() touches the screen, then refreshes it, *|
|* so curses(3X) doesn't get any chance to phlegm about with only doing *|
|* the bits that it thinks are OK. So this should fix up screens that *|
|* have been interfered with by biff(1), mesg(1) etc. *|
|* *|
\***************************************************************************/
void hoopy_refresh ()
{
clear ();
setup_screen ();
draw_board ();
update_scores ();
myrefresh ();
}
/***************************************************************************\
|* *|
|* The function print_msg() prints a short message centered on the *|
|* floor of the playing area, with a space before and after it. *|
|* If the message is NULL (ie. (char*)0), or null, (ie. ""), then *|
|* no message is printed. *|
|* *|
\***************************************************************************/
void print_msg (line)
char *line;
{
int i;
move (GAME_DEPTH, 2);
for (i = 0; i < 2*GAME_WIDTH; i++)
addch (FLOOR_CHAR);
if ((line != NULL) && (*line != '\0'))
mvaddstr (GAME_DEPTH, GAME_WIDTH+1-(strlen (line))/2,
form (" %s ", line));
myrefresh ();
}
/***************************************************************************\
|* *|
|* The function clear_area() fills the playing area with BLANK_CHARs. *|
|* It is used to clear the screen before each game, and also clearing *|
|* the screen while displaying pieces one at a time when in debug mode. *|
|* *|
\***************************************************************************/
void clear_area ()
{
int i, j, status, fastflag = 0;
fd_set fds;
struct timeval timeout;
static char buffer[LINELEN];
for (i = 0; i < GAME_DEPTH; i++) {
move (i, 2);
for (j = 0; j < 2*GAME_WIDTH; j++)
addch (BLANK_CHAR);
if (fastflag == 0) {
move (i+1, 2);
for (j = 0; j < 2*GAME_WIDTH; j++)
addch (FLOOR_CHAR);
myrefresh ();
FD_ZERO (&fds);
FD_SET (0, &fds);
timeout.tv_sec = 0L; /* Future implementations of select(2) */
timeout.tv_usec = 50000L; /* might change this value on return */
if ((status = select (1, &fds, (fd_set*) NULL, (fd_set*) NULL,
&timeout)) == -1)
if (errno != EINTR)
die (LE_SELECT, "select(2) failed in clear_area()");
if (status != 0) {
fastflag = 1;
(void) read (0, buffer, LINELEN);
}
}
}
move (GAME_DEPTH, 2);
for (j = 0; j < 2*GAME_WIDTH; j++)
addch (FLOOR_CHAR);
mvaddch (GAME_DEPTH, 0, CORNER_CHAR);
addch (CORNER_CHAR);
mvaddch (GAME_DEPTH, 2*GAME_WIDTH+2, CORNER_CHAR);
addch (CORNER_CHAR);
myrefresh ();
}
/***************************************************************************\
|* *|
|* The function setup_screen should be called exactly once, near the *|
|* beginning of execution. It initialises curses(3x), and prints the *|
|* game titles, the walls and the floor of the game area, and clears *|
|* this area using clear_area() (perhaps unsurprisingly) *|
|* *|
|* STOP PRESS: It no longer calls clear_area(), since play_game() *|
|* does that fairly immediately after this function returns. *|
|* *|
|* STOP PRESS^2: It now does do it again, since play_game() does the *|
|* sneaky-but-slow clear that it fine at times, but cruddy for *|
|* initialisation. *|
|* *|
\***************************************************************************/
void setup_screen ()
{
int i;
for (i = 0; i < GAME_DEPTH; i++) {
mvaddch (i, 0, WALL_CHAR);
addch (WALL_CHAR);
mvaddch (i, 2*GAME_WIDTH+2, WALL_CHAR);
addch (WALL_CHAR);
}
move (GAME_DEPTH, 2);
for (i = 0; i < 2*GAME_WIDTH; i++)
addch (FLOOR_CHAR);
mvaddch (GAME_DEPTH, 0, CORNER_CHAR);
addch (CORNER_CHAR);
mvaddch (GAME_DEPTH, 2*GAME_WIDTH+2, CORNER_CHAR);
addch (CORNER_CHAR);
attrset(A_BOLD | COLOR_PAIR(COLOR_YELLOW));
mvaddstr (0, 2*GAME_WIDTH+6, form ("%sTETRIS FOR TERMINALS%*s%s",
so_str, so_gunk, "", se_str));
attrset(A_BOLD | COLOR_PAIR(COLOR_GREEN));
mvaddstr (2, 2*GAME_WIDTH+6, "Written by Mike Taylor");
mvaddstr (3, 2*GAME_WIDTH+6, "Modified by Thomas Meisterburg");
mvaddstr (4, 2*GAME_WIDTH+6, "Started: Fri May 26 12:26:05 BST 1989");
attrset(A_BOLD | COLOR_PAIR(COLOR_RED));
mvaddstr (6, 2*GAME_WIDTH+6, form ("Game level: "));
mvaddstr (8, 2*GAME_WIDTH+6, "Score:");
mvaddstr (9, 2*GAME_WIDTH+6, "Pieces:");
mvaddstr (10, 2*GAME_WIDTH+6, "Levels:");
attrset(A_BOLD | COLOR_PAIR(COLOR_BLUE));
mvaddstr (12, 2*GAME_WIDTH+8, "Use keys:");
mvaddstr (13, 2*GAME_WIDTH+8, "=========");
mvaddstr (14, 2*GAME_WIDTH+8, "Move left: <left-arrow> ");
mvaddstr (15, 2*GAME_WIDTH+8, "Move right: <right-arrow>");
mvaddstr (16, 2*GAME_WIDTH+8, "Rotate: <up-arrow> ");
mvaddstr (17, 2*GAME_WIDTH+8, "Drop: <space bar> ");
mvaddstr (18, 2*GAME_WIDTH+8, "Pause: <p> ");
mvaddstr (19, 2*GAME_WIDTH+8, "Quit: <ESC> ");
mvaddstr (20, 2*GAME_WIDTH+8, "Refresh: <Ctrl>+<L> ");
attrset(A_NORMAL);
mvaddstr (6, 2*GAME_WIDTH+6 + strlen("Game level: "),
form ("%d", game_level));
}
/***************************************************************************\
|* *|
|* The function setup_curses should be called exactly once, near the *|
|* beginning of execution. It initialises curses(3x), and notes that *|
|* this has been done, by setting the global variable in_curses. *|
|* *|
\***************************************************************************/
void setup_curses ()
{
initscr ();
start_color();
clear ();
#ifndef LINT
noecho ();
cbreak ();
#endif /* LINT */
init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
keypad(stdscr, TRUE);
in_curses = 1;
}
/***************************************************************************\
|* *|
|* The function update_scores() puts the sepecified values of score, *|
|* no_pieces and no_levels on the screen in the specified positions. *|
|* *|
\***************************************************************************/
void update_scores ()
{
move (8, 34);
clrtoeol ();
addstr (form ("%d", score));
move (9, 34);
clrtoeol ();
addstr (form ("%d", no_pieces));
move (10, 34);
clrtoeol ();
addstr (form ("%d", no_levels));
}
/***************************************************************************\
|* *|
|* The function draw_board() puts the current state of the global array *|
|* board[] ontop the curses(3x) screen, then refresh()es it. *|
|* *|
\***************************************************************************/
void draw_board ()
{
int i, j;
for (i = 0; i < GAME_DEPTH; i++)
for (j = 0; j < GAME_WIDTH; j++)
if (board[i+4][j] == PI_EMPTY) {
mvaddch (i, 2*j+2, BLANK_CHAR);
addch (BLANK_CHAR);
}
else {
attrset(COLOR_PAIR(board[i+4][j] + 1));
mvaddstr (i, 2*j+2, pieces[board[i+4][j]].app);
attrset(A_NORMAL);
}
}
/***************************************************************************\
|* *|
|* The function draw_piece draws or erases one of the tetris pieces on *|
|* the screen in a specified orientation and position. The form of the *|
|* function is: *|
|* *|
|* draw_piece (piece_no, orientation, y, x, flag) *|
|* *|
|* All the arrguments are integers. Flag is either PD_DRAW or *|
|* PD_ERASE, specifying the effect of the function. Piece_no is *|
|* between 0 and 6 inclusive, and specifies what sort of piece is *|
|* required. Orientation is between 0 and 3 inclusive, and states *|
|* which way up the piece is to be drawn, and y and x express the *|
|* position as an index into the GAME_DEPTH by GAME_WIDTH array *|
|* that is the board. *|
|* *|
\***************************************************************************/
void draw_piece (piece_no, orient, y, x, flag)
int piece_no;
int orient;
int y;
int x;
int flag;
{
int i;
extern WINDOW *stdscr;
attrset(COLOR_PAIR(piece_no + 1));
for (i = 0; i < NO_SQUARES; i++)
if (y+pieces[piece_no].index[orient][i][0] >= 0)
if (flag == PD_ERASE) {
mvaddch (y+pieces[piece_no].index[orient][i][0],
(2*(x+pieces[piece_no].index[orient][i][1]))+2,
BLANK_CHAR);
addch (BLANK_CHAR);
}
else
mvaddstr (y+pieces[piece_no].index[orient][i][0],
(2*(x+pieces[piece_no].index[orient][i][1]))+2,
pieces[piece_no].app);
attrset(A_NORMAL);
}
/***************************************************************************\
|* *|
|* The function place_piece takes the same parameters as draw_piece, *|
|* except for the flag, and does not draw the piece, but places it *|
|* on the board. No checking is done to see if it will fit, since *|
|* should already have been done by can_place(). *|
|* *|
\***************************************************************************/
void place_piece (piece_no, orient, y, x)
int piece_no;
int orient;
int y;
int x;
{
int i;
for (i = 0; i < NO_SQUARES; i++)
board[y+4+pieces[piece_no].index[orient][i][0]]
[x+pieces[piece_no].index[orient][i][1]] = piece_no;
}
/***************************************************************************\
|* *|
|* The function can_place takes the same parameters as place_piece, *|
|* It does not draw the piece, nor place it on the board, but returns *|
|* an integer value -- 0 if the piece will not fit on the board, 1 if *|
|* it will (with the specified orientation, position, etc.) *|
|* *|
\***************************************************************************/
#define HERE(x) pieces[piece_no].index[orient][i][x]
int can_place (piece_no, orient, y, x)
int piece_no;
int orient;
int y;
int x;
{
int i;
for (i = 0; i < NO_SQUARES; i++)
if (((x+HERE(1) >= GAME_WIDTH) || /* Off right of screen or */
(x+HERE(1) < 0)) || /* Off left of screen */
(y+HERE(0) >= GAME_DEPTH) || /* Off bottom of screen */
(board[y+4+HERE(0)][x+HERE(1)] != PI_EMPTY))
/* Board position not empty */
return (0);
return (1);
}
/*-------------------------------------------------------------------------*/
tt-meista/tt.6 100644 764 144 6477 6451245321 12422 0 ustar thomas users .\" @(#)tt.6 1.7 89/06/05 SMI; from Mirksoft
.TH TT 6 "1 February 1983"
.SH NAME
tt \- a hoopy real-time puzzle-game.
.SH SYNOPSIS
.B tt
[
.B -s
|
.B -s#
] [
.B -b
] [
.B -l#
]
.SH DESCRIPTION
The program
.B tt
is an implementation of the well-known game
.B Tetris.
Quadominoes (groups of four squares joined orthogonally together) fall
slowly down the screen, accumulating at the bottom, and when the pile
reaches to top of the screen the game is over. The pieces may be
moved to the left or right, and rotated as they fall, with the aim of
making them tessellate with the pieces already at the bottom of the
game area. The height of the stack of pieces can be reduced by
filling a complete row of 10 squares, at which point that row will
disappear, and those above will fall down into its place. It is
possible (and desirable) to destroy multiple rows at once.
.LP
The keys with which these operations can be accomplished are displayed
on the screen during play. The game also recognises a suspend key and
a quit key, with which the game can be suspended or quit. Incredible,
huh? I mean, who would have thought
it?
.LP
In between games, when the program is waiting for a keypress before
restarting, pressing the <n> or <ESC> key will end the session.
.LP
The high-score table stores only a single score for each user at any
game-level. Thus a user exceeding his own level-0 high-score would
have his old entry in the high-score table (if any) replaced with the
new score. However, a single user may have multiple high-score table
entries for different game-levels.
.LP
The author recommends that the optimal game-levels are 0, 10 and -6
.SH FLAGS
.IP -s
If the
.B -s
flag is set, then
.B tt
will print the top 10 entries in the high-score table.
.IP -s#
If a number is specified, then
.B tt
will print that many high-score entries, up to a pre-defined maximum.
.IP -b
If the
.B -b
flag is set, then
.B tt
will rotate pieces backwards (ie. clockwise), for compatibility with
the grotty versions of Tetris available on the BBC micro and other such
machines. (The default anticlockwise rotation is compatible with the
.B tetris
and
.B mex
program mentioned below)
.IP -l#
If the
.B -l
flag is set, then
.B tt
will play on the level specified, which must be between -10 and 20.
Each level of play starts at the same speed, and increases in speed at
the same rate. They are differentiated only by the fact that non-zero
levels drop a number of pieces, equal to the absolute value of the
level, onto the screen before the game starts. Negative levels drop
pieces down the middle of the screen, positive levels place them
randomly.
.LP
The environment variable
.B TTNAME
contains, if set, the name which will be used in the high-score table,
if a good enough score is obtained to merit inclusion. If this
variable is not set,
.B tt
will use the environment variable
.B NAME
and if this is also not set, the user-code will be used.
.SH FILES
/usr/local/etc/ttscores \-- high-score table.
.br
/usr/local/etc/ttlock \-- lock file for high-score table.
.SH "SEE ALSO"
mundi(6), tetris(6), mex(6)
.SH AUTHOR
The program
.B tt
was written by Mike Taylor (mirk@uk.ac.warwick.cs) and modified
by Thomas Meisterburg (tmeisterburg@pader-online.de), based on the
original Tetris idea, by a frustratingly anonymous "Russian
Researcher".
.SH BUGS
None known \-- Please report any bugs to the author.
tt-meista/tt.c 100644 764 144 37007 6451250614 12511 0 ustar thomas users /***************************************************************************\
|*+-----------------------------------------------------------------------+*|
|*| |*|
|*| tt.c: A version of Tetris to run on ordinary terminals, |*|
|*| (ie., not needing a workstation, so should available |*|
|*| to peasant Newwords+ users. |*|
|*| |*|
|*| Author: Mike Taylor (mirk@uk.ac.warwick.cs) |*|
|*| Started: Fri May 26 12:26:05 BST 1989 |*|
|*| |*|
|*| Oooh look, I've just invented a new, chunkier kind of |*|
|*| comment-box. I can't decide yet whether I like it. |*|
|*| |*|
|*+-----------------------------------------------------------------------+*|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
/* Here goes what's needed to make this puppy work under Linux. */
#define F_TLOCK 2
#ifndef BADSIG
#define BADSIG -1
#endif /* BADSIG */
void l_bcopy (const void* s, void* d, int n)
{if (n > 0) memmove (d, s, (size_t) n);}
#include "tt.h"
#include "utils.h"
#include "screen.h"
#include "game.h"
/*-------------------------------------------------------------------------*/
extern time_t time ();
extern char *ctime ();
extern char *malloc ();
extern char *getenv ();
extern char *getlogin ();
/*-------------------------------------------------------------------------*/
int screen_depth; /* To be calculated by termcap(3) */
int screen_width; /* To be calculated by termcap(3) */
int so_gunk; /* To be calculated by termcap(3) */
int in_curses = 0; /* Set to 1 after initialisation */
int rotate_backwards = 0; /* If set non-zero, rotate clockwise */
int no_hiscores = 0; /* Number of hi-scores in the table */
int no_shown = NO_SHOWN; /* Number of hi-scores to list */
int game_level = 0; /* Number of free pieces */
int score; /* Accumulated game score */
int no_pieces; /* Number of pieces dropped so far */
int no_levels; /* Number of levels filled & deleted */
char prog_name[LINELEN]; /* Will be the basename of argv[0] */
char user_name[NAMELEN]; /* From environment: TTNAME or NAME */
char user_code[CODELEN]; /* From getpwuid(getuid())->pw_name */
int board[GAME_DEPTH+4][GAME_WIDTH];
struct score_ent hi_scores[NO_HISCORES];
char tc_string[LINELEN]; /* Needed as static storage for the awful */
char *so_str; /* ... tgetstr() function. so_str and ... */
char *se_str; /* ... se_str point into it. */
/***************************************************************************\
|* *|
|* This function is called if a SIGHUP, SIGINT or SIGTERM is caught, *|
|* and merely returns the user to standard terminal modes, (ie. exits *|
|* from curses(3X) before die()ing. *|
|* *|
\***************************************************************************/
void signal_end ()
{
if (in_curses)
print_msg ("Aborted!");
die (LE_OK, "");
}
/***************************************************************************\
|* *|
|* The function get_scores() reads the contents of the global array *|
|* hi_scores from the file named in the #definition of SCORE_FILE. *|
|* It also sets no_hiscores to the number of scores in the table. *|
|* *|
\***************************************************************************/
void get_scores ()
{
int fd;
struct stat stat_buf;
if ((fd = open (SCORE_FILE, O_RDONLY)) == -1) {
if (errno != ENOENT)
die (LE_OPEN, "couldn't open(2) high-score file for reading");
else {
no_hiscores = 0;
return;
}
}
if (fstat (fd, &stat_buf) == -1)
die (LE_STAT, "couldn't stat(2) high-score file");
no_hiscores = stat_buf.st_size/sizeof (struct score_ent);
if (read (fd, (char*) hi_scores, (int) stat_buf.st_size) == -1) {
(void) perror ("read()");
die (LE_READ, "couldn't read(2) high-score file");
}
(void) close (fd);
}
/***************************************************************************\
|* *|
|* The function print_scores() gets the table in from the disk_file, *|
|* and prints it in a nice, human-readable format. *|
|* *|
\***************************************************************************/
void print_scores ()
{
int i;
get_scores ();
if (no_hiscores == 0)
(void) puts ("There are no high-scores (yet!)");
else {
(void) puts ("+------+-------------------------+---------+-------+--------+--------+------+");
(void) puts ("| Rank | Name | Code | Score | Pieces | Levels | Type |");
(void) puts ("+------+-------------------------+---------+-------+--------+--------+------+");
for (i = 0; i < ((no_hiscores < no_shown) ? no_hiscores : no_shown); i++)
(void) printf ("|%5d | %-*.*s| %-8.8s|%6d |%7d |%7d |%5d |\n",
i+1, NAMELEN, NAMELEN, hi_scores[i].name,
hi_scores[i].code, hi_scores[i].score,
hi_scores[i].no_pieces, hi_scores[i].no_levels,
hi_scores[i].game_level);
(void) puts ("+------+-------------------------+---------+-------+--------+--------+------+");
}
}
/***************************************************************************\
|* *|
|* The function update_file puts a lock on the high-score-file, and *|
|* then (if successful), reads the high-scores, inserts the current *|
|* score in the table if it's good enough, and writes it back if it *|
|* has changed, finally removing the lock. It returns the player's *|
|* position in the table, or 0 if he is unplaced. If we were unable *|
|* to get the lock-file, it returns -1. *|
|* *|
|* I'm about to do some mods allowing compilation to be done with *|
|* the flag -DLOCKF to do the mutual exclusion using lockf(3) instead *|
|* a lock-file. This will be unneccesary on most systems, but here on *|
|* the Warwick systems, some users have titchy quotas that cause them *|
|* to be unable to create a lock-file, and thus to use the high-score *|
|* table. I'm using lockf(3) instead of flock(2) since it works across *|
|* machines. *|
|* *|
|* Apologies for the cruddy way I've written this function. It has had *|
|* bits added onto it in a very ad-hoc way, including the #ifdef'd bits *|
|* that determine what locking mechanism is used, and the result is, *|
|* shall we say, sub-optimal elegance. Particularly nasty is that way *|
|* that when the lockf(3)ing locking mechanism is used, we maintain *|
|* two open file-descriptors at once on the same file, but it's the *|
|* quickest and easiest way to use the existing get_scores() function, *|
|* and it does at least work. Since I don't anticipate anything else *|
|* significant being added to the function, I'm going to leave it as it *|
|* is, and not tidy it up unless I feel *really* guilty in the morning. *|
|* *|
\***************************************************************************/
int update_file ()
{
int i = 0, j = 0, k = 0; /* Sundry re-usable loop-indices */
int score_fd; /* The fd through which we write new file */
int lock_fd; /* If LOCKF is defined, we use this as */
/* an auxiliary fd on the SCORE_FILE, one */
/* that stays open all the time, so we can */
/* use lockf(). Otherwise, it is the fd */
/* that we open to the LOCK_FILE */
(void) umask (0000); /* 000 octal, just to make the point */
#ifdef LOCKF
if ((lock_fd = open (SCORE_FILE, O_RDWR | O_CREAT, 0666)) == -1)
die (LE_OPEN, "Couldn't open(2) score-file for lockf()");
while (i++ < 5) { /* Make up to five attempts */
if (lockf (lock_fd, F_TLOCK, 0) != -1)
break; /* If we succeed, then carry on */
/* Otherwise, if not due to exclusivity */
if (errno != EAGAIN) { /* then die with a system error */
print_msg ("lockf(3) error!");
(void) get_key ();
return (-1);
}
print_msg ("Hi-score access:");
sleep (1); /* Back off and wait ... */
print_msg (""); /* Then try again */
}
#else /* LOCKF */
while (i++ < 5) { /* Make up to five attempts */
if ((lock_fd = open (LOCK_FILE, O_CREAT | O_EXCL, 0666)) != -1)
break; /* If we succeed, then carry on */
/* Otherwise, if not due to exclusivity */
if (errno != EEXIST) { /* then die with a system error */
print_msg ("open(2) error!");
(void) get_key ();
return (-1);
}
print_msg ("Hi-score access:");
sleep (1); /* Back off and wait ... */
print_msg (""); /* Then try again */
}
#endif /* LOCKF */
if (i > 5) /* If we tried 5 times unsuccessfully, */
return (-1); /* Then give up and return -1 instead */
get_scores ();
for (i = 0; i < no_hiscores; i++) {
if (((!strcmp (user_code, hi_scores[i].code)) &&
(game_level == hi_scores[i].game_level)) &&
((score < hi_scores[i].score) ||
((score == hi_scores[i].score) &&
((no_pieces < hi_scores[i].no_pieces) ||
((no_pieces == hi_scores[i].no_pieces) &&
(no_levels < hi_scores[i].no_levels)))))) {
i = NO_HISCORES; /* If the same user has a better score */
break; /* on the same level, then drop this one */
}
if ((score > hi_scores[i].score) ||
((score == hi_scores[i].score) &&
((no_pieces > hi_scores[i].no_pieces) ||
((no_pieces == hi_scores[i].no_pieces) &&
((no_levels > hi_scores[i].no_levels) ||
((no_levels == hi_scores[i].no_levels) &&
((game_level >= hi_scores[i].game_level)))))))) /* Lisp :-) */
break; /* i is the new position of the player */
}
if (i == NO_HISCORES) { /* If we looped off the end of the array */
(void) close (lock_fd); /* then the score isn't good enough: */
#ifndef LOCKF /* Automagically removes advisory lockf() */
(void) unlink (LOCK_FILE);
#endif /* LOCKF */
return (0);
}
/* If there is a matching score lower down */
/* the file, set j to it, (otherwise to i) */
for (j = NO_HISCORES-1; j >= i; j--)
if ((!strcmp (user_code, hi_scores[j].code)) &&
(game_level == hi_scores[j].game_level))
break;
/* No duplicate score found, so just */
if (j < i) { /* shunt up all other scores. */
for (j = NO_HISCORES-1; j > i; j--)
l_bcopy ((char*) &hi_scores[j-1], (char*) &hi_scores[j],
sizeof (struct score_ent));
if (no_hiscores < NO_HISCORES)
no_hiscores++;
}
else { /* j points at a duplicate score of the */
for (k = j; k > i; k--) { /* new one, so shift bits between them */
l_bcopy ((char*) &hi_scores[k-1], (char*) &hi_scores[k],
sizeof (struct score_ent));
}
}
(void) strcpy (hi_scores[i].name, user_name);
(void) strcpy (hi_scores[i].code, user_code);
hi_scores[i].score = score;
hi_scores[i].no_pieces = no_pieces;
hi_scores[i].no_levels = no_levels;
hi_scores[i].game_level = game_level;
if ((score_fd = open (SCORE_FILE, O_WRONLY | O_CREAT, 0666)) == -1) {
perror ("open");
die (LE_OPEN, "couldn't open(2) score-file for writing");
}
if (write (score_fd, (char*) hi_scores, no_hiscores*sizeof
(struct score_ent)) == -1) {
perror ("write");
die (LE_WRITE, "couldn't write(2) to score-file");
}
(void) close (score_fd);
(void) close (lock_fd);
#ifndef LOCKF
(void) unlink (LOCK_FILE);
#endif /* LOCKF */
return (i+1);
}
/***************************************************************************\
|* *|
|* The function get_key() reads a character from the keyboard, and *|
|* performs some simple processing on it. If it's an 's', it lists *|
|* the high-score table. Otherwise, it returns 1 for a 'q' or 'n', *|
|* and 0 for anything else. *|
|* *|
\***************************************************************************/
int get_key ()
{
int ch;
ch = getch();
print_msg ("");
return ((ch == 'n') || (ch == 'N') || (ch == 27));
}
/***************************************************************************\
|* *|
|* The main() function handles initialisation, gets keys and names from *|
|* the environent, parses command-line arguments and so on. It then *|
|* goes into the main loop of calling play_game(), and asking if the *|
|* player wants another game, and so on. *|
|* *|
\***************************************************************************/
main (argc, argv)
int argc;
char **argv;
{
int i;
char *cp; /* Temporary pointer for getenv() */
time_t ignore_me; /* Storage for time for random seed. */
struct passwd *pw_ptr; /* Used with getuid() to find usercode */
(void) srandom ((int) time (&ignore_me));
(void) strcpy (prog_name, basename (argv[0]));
if ((i = getuid ()) == -1)
die (LE_GETUID, "couldn't getuid(2)");
if ((pw_ptr = getpwuid (i)) == NULL)
die (LE_GETPW, "couldn't get password entry");
(void) strncpy (user_code, pw_ptr->pw_name, CODELEN-1);
user_code[CODELEN-1] = '\0';
if ((cp = getenv ("TTNAME")) == NULL)
if ((cp = getenv ("NAME")) == NULL)
cp = user_code;
(void) strncpy (user_name, cp, NAMELEN-1);
user_name[NAMELEN-1] = '\0';
for (i = 1; i < argc; i++)
switch (argv[i][0]) {
case '-':
switch (argv[i][1]) {
case 's':
if (argv[i][2] != '\0')
if (((no_shown = atoi (argv[i]+2)) < 1) ||
(no_shown > NO_HISCORES)) {
static char tmp[LINELEN]; /* To stop recursive form() */
(void) sprintf (tmp, "Number of scores must be between 1 and %d",
NO_HISCORES);
die (LE_LEVEL, tmp);
}
print_scores (); /* Flag '-s': print scores, then exit. */
exit (LE_OK); /* Not in curses => No need to call die() */
case 'b':
rotate_backwards = 1;
break;
case 'l':
if (argv[i][2] == '\0')
goto USAGE_ERROR;
else
if (((game_level = atoi (argv[i]+2)) < -10) || (game_level > 20))
die (LE_LEVEL, "Game-level must be between -10 and 20");
break;
default:
goto USAGE_ERROR;
}
break;
default:
USAGE_ERROR:
die (LE_USAGE, form ("Usage: %s [options]\n\n"
"Options: -s[n] show highscores - If a number is specified,\n"
" n highscores will be displayed.\n"
" -b rotate pieces backwards\n"
" -l[n] play level n - n must be between -10 and 20.",
prog_name));
}
get_termcap ();
if (screen_depth < GAME_DEPTH+1)
die (LE_SCREEN, "screen is not deep enough");
if (screen_width < (2*GAME_WIDTH)+6+STAT_WIDTH)
die (LE_SCREEN, "screen is not wide enough");
if ((signal (SIGHUP, signal_end) == BADSIG) ||
(signal (SIGINT, signal_end) == BADSIG) ||
(signal (SIGQUIT, signal_end) == BADSIG) ||
(signal (SIGTERM, signal_end) == BADSIG))
die (LE_SIGNAL, "couldn't set up signal-handling");
setup_curses ();
show_intro();
setup_screen ();
clear_board ();
print_msg ("Press a key:");
if (get_key ())
die (LE_OK, "");
while (1) {
play_game ();
if ((i = update_file ()) > 0) {
static char tmp[LINELEN]; /* To stop recursive form() phelgming */
(void) sprintf (tmp, "Score ranks #%d", i);
print_msg (tmp);
}
if (i < 0)
print_msg ("Save-score failed!");
if (i != 0) {
flush_keyboard ();
if (get_key ())
break;
}
print_msg ("Again?");
flush_keyboard ();
if (get_key ())
break;
clear_area ();
}
die (LE_OK, "");
}
/*-------------------------------------------------------------------------*/
tt-meista/tt.h 100644 764 144 16667 6451250625 12531 0 ustar thomas users /***************************************************************************\
|* *|
|* tt.h: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
/***************************************************************************\
|* *|
|* The following macro, if defined, causes the exclusive high-score *|
|* file-locking to be done using lockf(3) instead of the O_EXCL flag *|
|* on open(2). This means that no temporary file need be created, and *|
|* thus, that no write-permission is needed on a lockfile-directory. *|
|* This should probably be used in preference to open(O_EXCL) when it *|
|* exists and is bugless. WARNING: on some machines, buggy versions *|
|* of lockf(3) can crash the machine, (eg. Sun3/4s running SunOS 4.0.1) *|
|* *|
\***************************************************************************/
#undef LOCKF /* Use lockf(3) instead of open(O_EXCL) */
/***************************************************************************\
|* *|
|* File in which high-scores will be stored. I'm not gonna muck *|
|* about encrypting them or anything, so you can either make tt *|
|* setuid, or just run the risk of stupid people editing the high- *|
|* score table by hand. *|
|* *|
\***************************************************************************/
#define LOCK_FILE "/usr/local/etc/ttlock"
#define SCORE_FILE "/usr/local/etc/ttscores"
/*-------------------------------------------------------------------------*/
#define NO_HISCORES 100 /* Size of high-score table */
#define NO_SHOWN 10 /* Default number to print */
#define LINELEN 160 /* Maximum length of a line of text */
#define NAMELEN 24 /* Multiple of four for struct-padding */
#define CODELEN 12 /* Multiple of four for struct-padding */
#define GAME_WIDTH 10 /* Number of squares across board */
#define GAME_DEPTH 20 /* Number of squares down board */
#define STAT_WIDTH 37 /* Number of characters for messages */
#define NO_PIECES 7 /* Number of different pieces */
#define NO_ORIENTS 4 /* Number of orientations possible */
#define NO_SQUARES 4 /* Max. number of squares per piece */
#define NO_DIMS 2 /* Number of dimensions of screen */
#define PI_EMPTY -1 /* Position of board is empty */
#define PI_SQUARE 0 /* Square piece */
#define PI_LONG 1 /* Long piece */
#define PI_ELL1 2 /* L-shaped piece */
#define PI_ELL2 3 /* Backwards L-shaped piece */
#define PI_TEE 4 /* T-shaped piece */
#define PI_ESS1 5 /* S-shaped piece */
#define PI_ESS2 6 /* Backwards S-shaped piece */
/***************************************************************************\
|* *|
|* The following #defines, (the LE_* ones) are local errors, in the *|
|* sense of being local to this program, as distinct from the system *|
|* errors, (ENOENT, EINTR and friends). They should be used as exit *|
|* statuses, as the first argument to die(), defined in utils.c *|
|* *|
\***************************************************************************/
#define LE_OK 0 /* No problems, me ole' fruit-bat */
#define LE_USAGE 1 /* Command-line syntax was wrong */
#define LE_ENV 2 /* Couldn't get environment variable */
#define LE_TERMCAP 3 /* Couldn't get a termcap entry */
#define LE_SCREEN 4 /* Screen was too small for game */
#define LE_SIGNAL 5 /* Signal(3) call failed */
#define LE_SELECT 6 /* Select(2) call failed */
#define LE_READ 7 /* Read(2) call failed */
#define LE_MALLOC 8 /* Couldn't allocate memory */
#define LE_LEVEL 9 /* User wanted to play on a silly level */
#define LE_OPEN 10 /* Open(2) call failed */
#define LE_STAT 11 /* {f,l,}stat(2) call failed */
#define LE_WRITE 12 /* write(2) call failed */
#define LE_GETUID 13 /* getuid(2) call failed */
#define LE_GETPW 14 /* getpwuid(3) call failed */
/***************************************************************************\
|* *|
|* This is the structure of which the high-score table is composed. *|
|* I'm gonna make everything a muliple of four bytes in an attempt *|
|* to maintain file-compatibility between sun3s and 4s (and maybe *|
|* other things, who knows?) This is also why all the integers are *|
|* long -- this helps to ensure that they will be four bytes long on *|
|* most architectures, (ie. including some that have 2-byte integers) *|
|* thus making struct-compatibility more likely. *|
|* *|
\***************************************************************************/
struct score_ent {
char name[NAMELEN]; /* Name of player (from environment) */
char code[CODELEN]; /* Code of player (from getpwuid()) */
long int score; /* Total score (5 for an "S" etc.) */
long int no_pieces; /* Number of pieces dropped in */
long int no_levels; /* Number of levels completed */
long int game_level; /* Number of free pieces at start */
};
/*-------------------------------------------------------------------------*/
extern int screen_depth; /* To be calculated by termcap(3) */
extern int screen_width; /* To be calculated by termcap(3) */
extern int so_gunk; /* To be calculated by termcap(3) */
extern int in_curses; /* Set to 1 after initialisation */
extern int rotate_backwards; /* If set non-zero, rotate clockwise */
extern int no_hiscores; /* Number of hi-scores in the table */
extern int game_level; /* Number of free pieces */
extern int score; /* Accumulated game score */
extern int no_pieces; /* Number of pieces dropped so far */
extern int no_levels; /* Number of levels filled & deleted */
extern char prog_name[LINELEN]; /* Will be the basename of argv[0] */
extern char user_name[NAMELEN]; /* From environment: TTNAME or NAME */
extern char user_code[CODELEN]; /* From getpwuid(getuid())->pw_name */
extern int board[GAME_DEPTH+4][GAME_WIDTH];
extern struct score_ent hi_scores[NO_HISCORES];
extern char tc_string[LINELEN]; /* Static, filled by termcap(3) */
extern char *so_str; /* Points at things found by termcap(3) */
extern char *se_str; /* Points at things found by termcap(3) */
/*-------------------------------------------------------------------------*/
extern void signal_die (); /* When CTRL-C etc. is pressed */
/*-------------------------------------------------------------------------*/
/***************************************************************************\
|* *|
|* The following macros will be defined if FD_ZERO is not defined by *|
|* the system. They simulate the behaviour of 4.3bsd's FD_ macros for *|
|* 4.2bsd machines, (ie. those running Berkeley UNIX, but of earlier *|
|* issue than 4.3; also SunOS, issues earlier than 4.0. *|
|* *|
|* This section added Fri Jun 23 20:18:20 BST 1989 *|
|* Written by Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* *|
\***************************************************************************/
#ifndef FD_ZERO
# define fd_set long int
# define FD_ZERO(fds_ptr) *(fds_ptr) = 0
# define FD_SET(fd, fds_ptr) *(fds_ptr) |= (1<<(fd))
#endif /* FD_ZERO */
/*-------------------------------------------------------------------------*/
tt-meista/Makefile 100644 764 144 2502 6451251037 13326 0 ustar thomas users # Makefile for "tt" - Tetris for Terminals (ie. not just workstations)
# ====================================================================
# Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
SOURCES = tt.c utils.c screen.c pieces.c game.c
HEADERS = tt.h utils.h screen.h pieces.h game.h
OBJECTS = tt.o utils.o screen.o pieces.o game.o
CC = gcc
LDFLAGS = -s
CFLAGS = -O2
LINTFLAGS = -abh -DLINT
LIBRARIES = -lncurses
TIDYUP = | egrep -v 'possible pointer alignment problem' \
| egrep -v 'long assignment may lose accuracy'
# ---------------------------------------------------------------------------
tt : $(OBJECTS)
$(CC) $(LDFLAGS) $(CFLAGS) $(OBJECTS) -o tt $(LIBRARIES)
ttl :
lint $(LINTFLAGS) $(SOURCES) $(TIDYUP)
lint :
lint $(LINTFLAGS) $(SOURCES)
tags :
etags *.[ch]
clean :
rm -f *.o tt a.out core
install : tt
@cp -v tt /usr/local/bin
@cp -v tt.6 /usr/man/man6
chmod a+rw /usr/local/etc
# --------------------------------------------------------------------------
tt.o : tt.c tt.h utils.h screen.h game.h
utils.o : utils.c utils.h tt.h screen.h
screen.o : screen.c screen.h tt.h pieces.h utils.h
pieces.o : pieces.c pieces.h tt.h
game.o : game.c game.h tt.h screen.h pieces.h utils.h
# --------------------------------------------------------------------------
tt-meista/game.h 100644 764 144 4120 6451250750 12747 0 ustar thomas users /***************************************************************************\
|* *|
|* game.h: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This is the module that *|
|* actually plays the game, (ie. moves things down the *|
|* screen, select(2)s on the keyboard, and so on) *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#include <curses.h>
#define LEFT_KEY KEY_LEFT /* Move piece left */
#define RIGHT_KEY KEY_RIGHT /* Move piece right */
#define ROTATE_KEY KEY_UP /* Rotate piece anticlockwise */
#define DROP_KEY ' ' /* Drop piece to bottom of screen */
#define SUSP_KEY 'p' /* Suspend. I'm sorry if its confusing */
#define QUIT_KEY 27 /* Quit. I'm sorry if its confusing */
#define CHEAT_KEY 'Z' /* Cheat (a bit) -- Might not help! */
#define REFRESH_KEY '\014' /* Control-L; refreshes screen */
/* This is hard-coded in. It can't */
/* be changed by setting TTKEYS, since */
/* the peasants couldn't handle all that */
/* "setenv TTKEYS `echo | tr`" carp(anag) */
#define NO_MOVES 4 /* Number of moves allowed per fall */
/*-------------------------------------------------------------------------*/
extern int left_key; /* Move piece left */
extern int right_key; /* Move piece right */
extern int rotate_key; /* Rotate piece anticlockwise */
extern int drop_key; /* Drop piece to bottom of screen */
extern int susp_key; /* Suspend. I'm sorry if its confusing */
extern int quit_key; /* Quit. I'm sorry if its confusing */
extern int cheat_key; /* Frogging identical comments :-P */
/*-------------------------------------------------------------------------*/
extern void clear_board ();
extern void play_game ();
/*-------------------------------------------------------------------------*/
tt-meista/pieces.h 100644 764 144 1627 6451250714 13317 0 ustar thomas users /***************************************************************************\
|* *|
|* pieces.h: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module contains the *|
|* definitions of the pieces. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
struct piece {
char app[3];
int points;
int index[NO_ORIENTS][NO_SQUARES][NO_DIMS];
};
/*-------------------------------------------------------------------------*/
extern struct piece pieces[NO_PIECES];
/*-------------------------------------------------------------------------*/
tt-meista/screen.h 100644 764 144 2651 6451250644 13326 0 ustar thomas users /***************************************************************************\
|* *|
|* screen.h: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module handles all *|
|* the icky curses(3x) bits. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#define WALL_CHAR '|' /* Character used for sides of pay-area */
#define FLOOR_CHAR '=' /* Character used for base-line */
#define CORNER_CHAR '+' /* Character used bottom left and right */
#define BLANK_CHAR ' ' /* Character used elsewhere */
#define PD_DRAW 0 /* Code to draw piece */
#define PD_ERASE 1 /* Code to erase piece */
/*-------------------------------------------------------------------------*/
extern void show_intro();
extern void myrefresh ();
extern void hoopy_refresh ();
extern void print_msg ();
extern void clear_area ();
extern void setup_screen ();
extern void setup_curses ();
extern void update_scores ();
extern void draw_board ();
extern void draw_piece ();
extern void place_piece ();
extern int can_place ();
/*-------------------------------------------------------------------------*/
tt-meista/README.Source 100644 764 144 2402 5210267451 14003 0 ustar thomas users Tetris for Terminals - "tt" - Written by Mike Taylor
====================================================
The ported to Linux. Hey Linus, mondo O/S!
We all know that a computer isn't worth the silicon it's made of unless it you
can play Tetris on it. So, here it is, after two long, hard days of work.
Actually, it only needed two defines and three procedures and I'm not even
sure why I had to put two of those procedures in there. It looks to me as
though those two are already in GCC 2.1. The third is usleep. You should just
be able to do a make install and have it work.
If you do thing manually, that is with a plane make, you will need to make
your /usr/local/etc directory read-writeable by all because that is where the
high score and lock files are kept. If you get an Open(2) error or
"Save-score failure" it is because this hasn't been done. If you want to
change these, edit lines 34-35 of tt.h. Also in game.h, in lines 14-21 you can
change the key assignments, ie. right, left, drop.
That's about it for what I have to say. I can be reached on the InterNet as
"bairstow@vivaldi.psu.edu" or "sab139@psuvm.psu.edu" and on BitNet as
"sab139 AT psuvm." I also read comp.os.linux so if you post something there I
should catch it.
-- Steve Bairstow
tt-meista/utils.c 100644 764 144 15541 6451250670 13223 0 ustar thomas users /***************************************************************************\
|* *|
|* utils.c: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module supplies all *|
|* the function that I will need, but which are not *|
|* intrinsically a part of Tetris itself. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
#include <curses.h>
#include <term.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#ifdef va_start /* Please do not ask why my compiler */
#undef va_start /* needs this to compile correctly :) */
#endif /* va_start */
#include <varargs.h>
/* Stuff needed to run under Linux */
char* l_rindex (const char* s, int c)
{return strrchr (s, c);}
#include "tt.h"
#include "utils.h"
#include "screen.h"
/*-------------------------------------------------------------------------*/
extern char *rindex ();
extern char *getenv ();
/***************************************************************************\
|* *|
|* The function basename() acts like the UNIX(tm) command of the same *|
|* name. It takes as its argument a string, and returns the last path- *|
|* component of that string, (ie, the section after the last '/'), or *|
|* the whole string if it has no '/'. *|
|* *|
\***************************************************************************/
char *basename (name)
char *name;
{
char *slash;
if (slash = l_rindex (name, '/'))
return (slash+1);
else
return (name);
}
/***************************************************************************\
|* *|
|* This is my home-made varargs-based version of C++'s neat function of *|
|* the same name. It saves all that tedious mucking about with static *|
|* buffers which you sprintf into, then forget all about, by basically *|
|* being a sprintf with its own statis storage. It takes as arguments *|
|* exactly the same things as printf(3), and returns a pointer to the *|
|* resultant string. *|
|* *|
\***************************************************************************/
/*VARARGS*/
char *form (va_alist)
va_dcl
{
va_list pvar;
char *fmt_string;
static char result[LINELEN];
va_start (pvar);
fmt_string = va_arg (pvar, char*);
(void) vsprintf (result, fmt_string, pvar);
va_end (pvar);
return (result);
}
/***************************************************************************\
|* *|
|* This is yer bog-standard "print a message and quit" function, except *|
|* that it checks to see if we are in curses(3x), and if so, takes us *|
|* out before doing its stuff. The arguments are an integer, the exit *|
|* status, and a string containing an error report, to which will be *|
|* prepended the program name when it is printed. If the status code *|
|* is LE_OK, (ie. nothing went wrong), no message is printed. *|
|* *|
\***************************************************************************/
void die (status, line)
int status;
char *line;
{
if (in_curses) {
myrefresh ();
#ifndef LINT
nocbreak ();
echo ();
#endif /* LINT */
endwin ();
}
if (status != LE_OK)
(void) printf ("%s: %s\n", prog_name, line);
#ifndef LOCKF
(void) unlink (LOCK_FILE); /* Just in case :-) */
#endif /* LOCKF */
exit (status);
}
/***************************************************************************\
|* *|
|* The function get_termcap extracts the information we need from the *|
|* UNIX terminal capability database. It has been (correctly) pointed *|
|* out that most of what I do in this function is already done for me *|
|* by curses(3x), but (A) curses behaves stupidly on terminals with *|
|* termcap's "sg" non-zero, (ie. refuses to enter standout mode, so *|
|* you *have* to do it manually), and (B) I wanted to prove a point to *|
|* Sunny and others, who said it was impossible. Tgets() sucks :-P *|
|* *|
\***************************************************************************/
void get_termcap ()
{
static char bp[1024]; /* Seems odd to hardwire the 1024, but ... */
char *term_name; /* ... the manual entry told me to do it! */
int status; /* Success/failure of tgetent here */
char *tmp_ptr = tc_string; /* Pointer to be advanced by tgetstr() */
if ((term_name = getenv ("TERM")) == NULL)
die (LE_ENV, "couldn't get TERM from environment");
if ((status = tgetent (bp, term_name)) == -1)
die (LE_TERMCAP, "couldn't open TERMCAP file");
if (status == 0)
die (LE_TERMCAP, "couldn't find your terminal in TERMCAP file");
if ((screen_depth = tgetnum ("li")) == -1)
die (LE_TERMCAP, "couldn't get screen-depth from termcap");
if ((screen_width = tgetnum ("co")) == -1)
die (LE_TERMCAP, "couldn't get screen-width from termcap");
if ((so_gunk = tgetnum ("sg")) == -1)
so_gunk = 0; /* Default value */
so_str = tmp_ptr;
if (tgetstr ("so", &tmp_ptr) == NULL)
die (LE_TERMCAP, "couldn't get standout code from termcap");
se_str = tmp_ptr;
if (tgetstr ("se", &tmp_ptr) == NULL)
die (LE_TERMCAP, "couldn't get standend code from termcap");
}
/***************************************************************************\
|* *|
|* The function flush_keybord() checks to see whether there are any *|
|* keystrokes waiting to be read, and if so reads them and throws *|
|* them away. Surprising how complex this needs to be, but the only *|
|* alternative I can think of is to ioctl() stdin to be non-blocking, *|
|* read() until it returns 0, then ioctl() it back, which is scarcely *|
|* an improvement. *|
|* *|
\***************************************************************************/
void flush_keyboard ()
{
fd_set fds; /* Will contain only stdin */
struct timeval timeout; /* Will contain a zero timeout (poll) */
static char buffer[LINELEN]; /* Will be used as the buffer to read(2) */
int status; /* Contains select(2)'s return value */
while (1) {
FD_ZERO (&fds);
FD_SET (0, &fds);
timeout.tv_sec = 0L;
timeout.tv_usec = 0L;
if (((status = select (1, &fds, (fd_set*) NULL, (fd_set*) NULL,
&timeout)) == -1) && (errno != EINTR))
die (LE_SELECT, "select(2) failed in flush_keybord()");
if ((status == 0) || (status == -1))
break; /* Can only be -1 if errno == EINTR */
(void) read (0, buffer, LINELEN);
}
}
/*-------------------------------------------------------------------------*/
tt-meista/utils.h 100644 764 144 1607 6451250677 13215 0 ustar thomas users /***************************************************************************\
|* *|
|* utils.h: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module supplies all *|
|* the function that I will need, but which are not *|
|* intrinsically a part of Tetris itself. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
/*
* Modified by Thomas Meisterburg <tmeisterburg@pader-online.de>
*
*/
extern char *basename ();
extern char *form ();
extern void die ();
extern void get_termcap ();
extern void flush_keyboard ();
/*-------------------------------------------------------------------------*/
tt-meista/README.Meista 100644 764 144 1056 6451250373 13774 0 ustar thomas users Tetris for Terminals - "tt" - Written by Mike Taylor
====================================================
This is my modified version of Tetris for Terminals... :)
What's new?
-----------
- no more compiler-warnings with newer versions of gcc
(I tested it with gcc 2.7.2.1)
- color support
- arrow-key support
- an intro screen :-)
If you have any problems with these new functions, tell me about it!
My eMail-Address is "tmeisterburg@pader-online.de"...
Thomas Meisterburg
Dec. 27th, 1997
By the way: You can cheat by pressing <Shift>+<z>. :->
tt-meista/tt 100755 764 144 42140 6451251050 12260 0 ustar thomas users ELF 4 A 4 ( 4 44 ԀԀ 5 5 6 ` 8* >