Imported Debian patch 0.52-6
[pkg/netris.git] / game.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
20  */
21
22 #define NOEXT
23 #include "netris.h"
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <netinet/in.h>
29
30 enum { KT_left, KT_rotate, KT_right, KT_drop, KT_down,
31         KT_toggleSpy, KT_pause, KT_faster, KT_redraw, KT_new, KT_numKeys };
32
33 static char *keyNames[KT_numKeys+1] = {
34         "Left", "Rotate", "Right", "Drop", "Down", "ToggleSpy", "Pause",
35         "Faster", "Redraw", "New", NULL };
36
37 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
38
39 static char keyTable[KT_numKeys+1];
40 static int dropModeEnable = 0;
41 static char *robotProg;
42
43 static int wonLast = 0;
44 int lost = 0, won = 0;
45 enum States gameState = STATE_STARTING;
46
47 ExtFunc void MapKeys(char *newKeys)
48 {
49         int i, k, ch;
50         char used[256];
51         int errs = 0;
52
53         /* XXX assumptions about ASCII encoding here */
54         for (i = k = 0; newKeys[i] && k < KT_numKeys; i++,k++) {
55                 if (newKeys[i] == '^' && newKeys[i+1])
56                         keyTable[k] = toupper(newKeys[++i]) - ('A' - 1);
57                 else
58                         keyTable[k] = newKeys[i];
59         }
60         memset(used, 0, sizeof(used));
61         for (k = 0; k < KT_numKeys; k++) {
62                 ch = (unsigned char) keyTable[k];
63                 if (used[ch]) {
64                         if (iscntrl(ch) && ch < ' ')
65                                 sprintf(scratch, "Ctrl-%c", ch + ('A' - 1));
66                         else if (isprint(ch))
67                                 sprintf(scratch, "\"%c\"", ch);
68                         else
69                                 sprintf(scratch, "0x%X", ch);
70                         if (!errs)
71                                 fprintf(stderr, "Duplicate key mappings:\n");
72                         errs++;
73                         fprintf(stderr, "  %s mapped to both %s and %s\n",
74                                         scratch, keyNames[used[ch]-1], keyNames[k]);
75                 }
76                 used[ch] = k + 1;
77         }
78         if (errs)
79                 exit(1);
80 }
81
82 ExtFunc int StartNewPiece(int scr, Shape *shape)
83 {
84         curShape[scr] = shape;
85         curY[scr] = boardVisible[scr] + 4;
86         curX[scr] = boardWidth[scr] / 2;
87         while (!ShapeVisible(shape, scr, curY[scr], curX[scr]))
88                 --curY[scr];
89         if (!ShapeFits(shape, scr, curY[scr], curX[scr]))
90                 return 0;
91         PlotShape(shape, scr, curY[scr], curX[scr], 1);
92         return 1;
93 }
94
95 ExtFunc void OneGame(int scr, int scr2)
96 {
97         MyEvent event;
98         int linesCleared, changed = 0;
99         int spied = 0, spying = 0, dropMode = 0;
100         int oldPaused = 0, paused = 0, pausedByMe = 0, pausedByThem = 0;
101         long pauseTimeLeft;
102         int pieceCount = 0;
103         int key;
104         char *p, *cmd;
105
106         myLinesCleared = enemyLinesCleared = 0;
107         speed = stepDownInterval;
108         ResetBaseTime();
109         InitBoard(scr);
110         if (scr2 >= 0) {
111                 spied = 1;
112                 spying = 1;
113                 InitBoard(scr2);
114                 UpdateOpponentDisplay();
115         }
116         ShowDisplayInfo();
117         SetITimer(speed, speed);
118         if (robotEnable) {
119                 RobotCmd(0, "GameType %s\n", gameNames[game]);
120                 RobotCmd(0, "BoardSize 0 %d %d\n",
121                                 boardVisible[scr], boardWidth[scr]);
122                 if (scr2 >= 0) {
123                         RobotCmd(0, "BoardSize 1 %d %d\n",
124                                         boardVisible[scr2], boardWidth[scr2]);
125                         RobotCmd(0, "Opponent 1 %s %s\n", opponentName, opponentHost);
126                         if (opponentFlags & SCF_usingRobot)
127                                 RobotCmd(0, "OpponentFlag 1 robot\n");
128                         if (opponentFlags & SCF_fairRobot)
129                                 RobotCmd(0, "OpponentFlag 1 fairRobot\n");
130                 }
131                 RobotCmd(0, "TickLength %.3f\n", speed / 1.0e6);
132                 RobotCmd(0, "BeginGame\n");
133                 RobotTimeStamp();
134         }
135         while (StartNewPiece(scr, ChooseOption(stdOptions))) {
136                 if (robotEnable && !fairRobot)
137                         RobotCmd(1, "NewPiece %d\n", ++pieceCount);
138                 if (spied) {
139                         short shapeNum;
140                         netint2 data[1];
141
142                         shapeNum = ShapeToNetNum(curShape[scr]);
143                         data[0] = hton2(shapeNum);
144                         SendPacket(NP_newPiece, sizeof(data), data);
145                 }
146                 for (;;) {
147                         changed = RefreshBoard(scr) || changed;
148                         if (spying)
149                                 changed = RefreshBoard(scr2) || changed;
150                         if (changed) {
151                                 RefreshScreen();
152                                 changed = 0;
153                         }
154                         CheckNetConn();
155                         switch (WaitMyEvent(&event, EM_any)) {
156                                 case E_alarm:
157                                         if (!MovePiece(scr, -1, 0))
158                                                 goto nextPiece;
159                                         else if (spied)
160                                                 SendPacket(NP_down, 0, NULL);
161                                         break;
162                                 case E_key:
163                                         p = strchr(keyTable, tolower(event.u.key));
164                                         key = p - keyTable;
165                                         if (robotEnable) {
166                                                 RobotCmd(1, "UserKey %d %s\n",
167                                                                 (int)(unsigned char)event.u.key,
168                                                                 p ? keyNames[key] : "?");
169                                                 break;
170                                         }
171                                         if (!p)
172                                                 break;
173                                 keyEvent:
174                                         if (paused && (key != KT_pause) && (key != KT_redraw))
175                                                 break;
176                                         switch(key) {
177                                                 case KT_left:
178                                                         if (MovePiece(scr, 0, -1) && spied)
179                                                                 SendPacket(NP_left, 0, NULL);
180                                                         break;
181                                                 case KT_right:
182                                                         if (MovePiece(scr, 0, 1) && spied)
183                                                                 SendPacket(NP_right, 0, NULL);
184                                                         break;
185                                                 case KT_rotate:
186                                                         if (RotatePiece(scr) && spied)
187                                                                 SendPacket(NP_rotate, 0, NULL);
188                                                         break;
189                                                 case KT_down:
190                                                         if (MovePiece(scr, -1, 0) && spied)
191                                                                 SendPacket(NP_down, 0, NULL);
192                                                         break;
193                                                 case KT_toggleSpy:
194                                                         spying = (!spying) && (scr2 >= 0);
195                                                         break;
196                                                 case KT_drop:
197                                                         if (DropPiece(scr) > 0) {
198                                                                 if (spied)
199                                                                         SendPacket(NP_drop, 0, NULL);
200                                                                 SetITimer(speed, speed);
201                                                         }
202                                                         dropMode = dropModeEnable;
203                                                         break;
204                                                 case KT_pause:
205                                                         pausedByMe = !pausedByMe;
206                                                         if (game == GT_classicTwo) {
207                                                                 netint2 data[1];
208
209                                                                 data[0] = hton2(pausedByMe);
210                                                                 SendPacket(NP_pause, sizeof(data), data);
211                                                         }
212                                                         paused = pausedByMe || pausedByThem;
213                                                         if (robotEnable)
214                                                                 RobotCmd(1, "Pause %d %d\n", pausedByMe,
215                                                                         pausedByThem);
216                                                         ShowPause(pausedByMe, pausedByThem);
217                                                         changed = 1;
218                                                         break;
219                                                 case KT_faster:
220                                                         if (game != GT_onePlayer)
221                                                                 break;
222                                                         speed = speed * 0.8;
223                                                         SetITimer(speed, SetITimer(0, 0));
224                                                         ShowDisplayInfo();
225                                                         changed = 1;
226                                                         break;
227                                                 case KT_redraw:
228                                                         ScheduleFullRedraw();
229                                                         if (paused)
230                                                                 RefreshScreen();
231                                                         break;
232                                         }
233                                         if (dropMode && DropPiece(scr) > 0) {
234                                                 if (spied)
235                                                         SendPacket(NP_drop, 0, NULL);
236                                                 SetITimer(speed, speed);
237                                         }
238                                         break;
239                                 case E_robot:
240                                 {
241                                         int num;
242
243                                         cmd = event.u.robot.data;
244                                         if ((p = strchr(cmd, ' ')))
245                                                 *p++ = 0;
246                                         else
247                                                 p = cmd + strlen(cmd);
248                                         for (key = 0; keyNames[key]; ++key)
249                                                 if (!strcmp(keyNames[key], cmd) &&
250                                                                 (fairRobot || (1 == sscanf(p, "%d", &num) &&
251                                                                         num == pieceCount)))
252                                                         goto keyEvent;
253                                         if (!strcmp(cmd, "Message")) {
254                                                 Message(p);
255                                                 changed = 1;
256                                         }
257                                         break;
258                                 }
259                                 case E_net:
260                                         switch(event.u.net.type) {
261                                                 case NP_giveJunk:
262                                                 {
263                                                         netint2 data[2];
264                                                         short column;
265
266                                                         memcpy(data, event.u.net.data, sizeof(data[0]));
267                                                         column = Random(0, boardWidth[scr]);
268                                                         data[1] = hton2(column);
269                                                         InsertJunk(scr, ntoh2(data[0]), column);
270                                                         if (spied)
271                                                                 SendPacket(NP_insertJunk, sizeof(data), data);
272                                                         break;
273                                                 }
274                                                 case NP_newPiece:
275                                                 {
276                                                         short shapeNum;
277                                                         netint2 data[1];
278
279                                                         FreezePiece(scr2);
280                                                         memcpy(data, event.u.net.data, sizeof(data));
281                                                         shapeNum = ntoh2(data[0]);
282                                                         StartNewPiece(scr2, NetNumToShape(shapeNum));
283                                                         break;
284                                                 }
285                                                 case NP_down:
286                                                         MovePiece(scr2, -1, 0);
287                                                         break;
288                                                 case NP_left:
289                                                         MovePiece(scr2, 0, -1);
290                                                         break;
291                                                 case NP_right:
292                                                         MovePiece(scr2, 0, 1);
293                                                         break;
294                                                 case NP_rotate:
295                                                         RotatePiece(scr2);
296                                                         break;
297                                                 case NP_drop:
298                                                         DropPiece(scr2);
299                                                         break;
300                                                 case NP_clear:
301                                                         {
302                                                                 int cleared = ClearFullLines(scr2);
303                                                                 if (cleared) {
304                                                                         enemyLinesCleared += cleared;
305                                                                         enemyTotalLinesCleared += cleared;
306                                                                         ShowDisplayInfo();
307                                                                         RefreshScreen();
308                                                                 }
309                                                         }
310                                                         break;
311                                                 case NP_insertJunk:
312                                                 {
313                                                         netint2 data[2];
314
315                                                         memcpy(data, event.u.net.data, sizeof(data));
316                                                         InsertJunk(scr2, ntoh2(data[0]), ntoh2(data[1]));
317                                                         break;
318                                                 }
319                                                 case NP_pause:
320                                                 {
321                                                         netint2 data[1];
322
323                                                         memcpy(data, event.u.net.data, sizeof(data));
324                                                         pausedByThem = ntoh2(data[0]);
325                                                         paused = pausedByMe || pausedByThem;
326                                                         if (robotEnable)
327                                                                 RobotCmd(1, "Pause %d %d\n", pausedByMe,
328                                                                         pausedByThem);
329                                                         ShowPause(pausedByMe, pausedByThem);
330                                                         changed = 1;
331                                                         break;
332                                                 }
333                                                 default:
334                                                         break;
335                                         }
336                                         break;
337                                 case E_lostRobot:
338                                 case E_lostConn:
339                                         wonLast = 1;
340                                         goto gameOver;
341                                 default:
342                                         break;
343                         }
344                         if (paused != oldPaused) {
345                                 if (paused)
346                                         pauseTimeLeft = SetITimer(0, 0);
347                                 else
348                                         SetITimer(speed, pauseTimeLeft);
349                                 oldPaused = paused;
350                         }
351                 }
352         nextPiece:
353                 dropMode = 0;
354                 FreezePiece(scr);
355                 myLinesCleared += linesCleared = ClearFullLines(scr);           
356                 myTotalLinesCleared += linesCleared;
357                 if (linesCleared) {
358                         ShowDisplayInfo();
359                         RefreshScreen();
360                 }
361                 if (linesCleared > 0 && spied)
362                         SendPacket(NP_clear, 0, NULL);
363                 if (game == GT_classicTwo && linesCleared > 1) {
364                         short junkLines;
365                         netint2 data[1];
366
367                         junkLines = linesCleared - (linesCleared < 4);
368                         data[0] = hton2(junkLines);
369                         SendPacket(NP_giveJunk, sizeof(data), data);
370                 }
371         }
372         wonLast = 0;
373
374 gameOver:
375         SetITimer(0, 0);
376 }
377
378 ExtFunc int main(int argc, char **argv)
379 {
380         int initConn = 0, waitConn = 0, ch, done = 0;
381         char *hostStr = NULL, *portStr = NULL;
382         MyEvent event;
383
384         standoutEnable = colorEnable = 1;
385         stepDownInterval = DEFAULT_INTERVAL;
386         MapKeys(DEFAULT_KEYS);
387         while ((ch = getopt(argc, argv, "hHRs:r:Fk:c:woDSCp:i:")) != -1)
388                 switch (ch) {
389                         case 'c':
390                                 initConn = 1;
391                                 hostStr = optarg;
392                                 break;
393                         case 'w':
394                                 waitConn = 1;
395                                 break;
396                         case 'p':
397                                 portStr = optarg;
398                                 break;
399                         case 'i':
400                                 stepDownInterval = atof(optarg) * 1e6;
401                                 break;
402                         case 's':
403                                 initSeed = atoi(optarg);
404                                 myFlags |= SCF_setSeed;
405                                 break;
406                         case 'r':
407                                 robotEnable = 1;
408                                 robotProg = optarg;
409                                 myFlags |= SCF_usingRobot;
410                                 break;
411                         case 'F':
412                                 fairRobot = 1;
413                                 myFlags |= SCF_fairRobot;
414                                 break;
415                         case 'D':
416                                 dropModeEnable = 1;
417                                 break;
418                         case 'C':
419                                 colorEnable = 0;
420                                 break;
421                         case 'S':
422                                 standoutEnable = 0;
423                                 break;
424                         case 'k':
425                                 MapKeys(optarg);
426                                 break;
427                         case 'H':
428                                 DistInfo();
429                                 exit(0);
430                         case 'R':
431                                 Rules();
432                                 exit(0);
433                         case 'h':
434                                 Usage();
435                                 exit(0);
436                         default:
437                                 Usage();
438                                 exit(1);
439                 }
440         if (optind < argc || (initConn && waitConn)) {
441                 Usage();
442                 exit(1);
443         }
444         if (fairRobot && !robotEnable)
445                 fatal("You can't use the -F option without the -r option");
446         InitUtil();
447         InitScreens();  
448         while(!done) {
449                 if (robotEnable)
450                         InitRobot(robotProg);
451                 InitNet();
452                 if (!initSeed)
453                         SRandom(time(0));
454                 if (initConn || waitConn) {
455                         game = GT_classicTwo;
456                         if(gameState != STATE_STARTING) {
457                                 gameState = STATE_WAIT_CONNECTION;
458                                 ShowDisplayInfo();
459                                 RefreshScreen();
460                         }
461                         if (initConn)
462                                 InitiateConnection(hostStr, portStr);
463                         else if (waitConn)
464                                 WaitForConnection(portStr);
465                         gameState = STATE_PLAYING;
466                         ShowDisplayInfo();
467                         RefreshScreen();
468                         {
469                                 netint4 data[2];
470                                 int major;
471                                 
472                                 data[0] = hton4(MAJOR_VERSION);
473                                 data[1] = hton4(PROTOCOL_VERSION);
474                                 SendPacket(NP_version, sizeof(data), data);
475                                 if (WaitMyEvent(&event, EM_net) != E_net)
476                                         fatal("Network negotiation failed");
477                                 memcpy(data, event.u.net.data, sizeof(data));
478                                 major = ntoh4(data[0]);
479                                 protocolVersion = ntoh4(data[1]);
480                                 if (event.u.net.type != NP_version || major < MAJOR_VERSION)
481                                         fatal("Your opponent is using an old, incompatible version\n"
482                                               "of Netris.  They should get the latest version.");
483                                 if (major > MAJOR_VERSION)
484                                         fatal("Your opponent is using an newer, incompatible version\n"
485                                               "of Netris.  Get the latest version.");
486                                 if (protocolVersion > PROTOCOL_VERSION)
487                                         protocolVersion = PROTOCOL_VERSION;
488                         }
489                         if (protocolVersion < 3 && stepDownInterval != DEFAULT_INTERVAL)
490                                 fatal("Your opponent's version of Netris predates the -i option.\n"
491                                       "For fairness, you shouldn't use the -i option either.");
492                         {
493                                 netint4 data[3];
494                                 int len;
495                                 int seed;
496                                 
497                                 if (protocolVersion >= 3)
498                                         len = sizeof(data);
499                                 else
500                                         len = sizeof(netint4[2]);
501                                 if ((myFlags & SCF_setSeed))
502                                         seed = initSeed;
503                                 else
504                                         seed = time(0);
505                                 if (waitConn)
506                                         SRandom(seed);
507                                 data[0] = hton4(myFlags);
508                                 data[1] = hton4(seed);
509                                 data[2] = hton4(stepDownInterval);
510                                 SendPacket(NP_startConn, len, data);
511                                 if (WaitMyEvent(&event, EM_net) != E_net ||
512                                     event.u.net.type != NP_startConn)
513                                         fatal("Network negotiation failed");
514                                 memcpy(data, event.u.net.data, len);
515                                 opponentFlags = ntoh4(data[0]);
516                                 seed = ntoh4(data[1]);
517                                 if (initConn) {
518                                         if ((opponentFlags & SCF_setSeed) != (myFlags & SCF_setSeed))
519                                                 fatal("If one player sets the random number seed, "
520                                                       "both must.");
521                                         if ((myFlags & SCF_setSeed) && seed != initSeed)
522                                                 fatal("Both players have set the random number seed, "
523                                                       "and they are unequal.");
524                                         if (protocolVersion >= 3 && stepDownInterval != ntoh4(data[2]))
525                                                 fatal("Your opponent is using a different step-down "
526                                                       "interval (-i).\nYou must both use the same one.");
527                                         SRandom(seed);
528                                 }
529                         }
530                         {
531                                 char *userName;
532                                 int len, i;
533
534                                 userName = getenv("LOGNAME");
535                                 if (!userName || !userName[0])
536                                         userName = getenv("USER");
537                                 if (!userName || !userName[0])
538                                         strcpy(userName, "???");
539                                 len = strlen(userName)+1;
540                                 if (len > sizeof(opponentName))
541                                         len = sizeof(opponentName);
542                                 SendPacket(NP_userName, len, userName);
543                                 if (WaitMyEvent(&event, EM_net) != E_net ||
544                                     event.u.net.type != NP_userName)
545                                         fatal("Network negotiation failed");
546                                 strncpy(opponentName, event.u.net.data,
547                                         sizeof(opponentName)-1);
548                                 opponentName[sizeof(opponentName)-1] = 0;
549                                 for (i = 0; opponentName[i]; ++i)
550                                         if (!isprint(opponentName[i]))
551                                                 opponentName[i] = '?';
552                                 for (i = 0; opponentHost[i]; ++i)
553                                         if (!isprint(opponentHost[i]))
554                                                 opponentHost[i] = '?';
555                         }
556                         OneGame(0, 1);
557                 }
558                 else {
559                         game = GT_onePlayer;
560                         OneGame(0, -1);
561                 }
562                 if (wonLast) {
563                         won++;
564                 } else {
565                         lost++;
566                         WaitMyEvent(&event, EM_net);
567                 }
568                 CloseNet();
569                 if (robotEnable) {
570                         CloseRobot();
571                 } else {
572                         gameState = STATE_WAIT_KEYPRESS;
573                         ShowDisplayInfo();
574                         RefreshScreen();
575                         while(getchar() != keyTable[KT_new])
576                                 ;
577                 }
578         }
579
580         return 0;
581 }
582
583 /*
584  * vi: ts=4 ai
585  * vim: noai si
586  */