Imported Upstream version 0.4
[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.38 1996/02/09 08:22:11 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_numKeys };
32
33 static char *keyNames[KT_numKeys+1] = {
34         "Left", "Rotate", "Right", "Drop", "Down", "ToggleSpy", "Pause",
35         "Faster", "Redraw", 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 ExtFunc void MapKeys(char *newKeys)
44 {
45         int i, k, ch;
46         char used[256];
47         int errs = 0;
48
49         /* XXX assumptions about ASCII encoding here */
50         for (i = k = 0; newKeys[i] && k < KT_numKeys; i++,k++) {
51                 if (newKeys[i] == '^' && newKeys[i+1])
52                         keyTable[k] = toupper(newKeys[++i]) - ('A' - 1);
53                 else
54                         keyTable[k] = newKeys[i];
55         }
56         memset(used, 0, sizeof(used));
57         for (k = 0; k < KT_numKeys; k++) {
58                 ch = (unsigned char) keyTable[k];
59                 if (used[ch]) {
60                         if (iscntrl(ch) && ch < ' ')
61                                 sprintf(scratch, "Ctrl-%c", ch + ('A' - 1));
62                         else if (isprint(ch))
63                                 sprintf(scratch, "\"%c\"", ch);
64                         else
65                                 sprintf(scratch, "0x%X", ch);
66                         if (!errs)
67                                 fprintf(stderr, "Duplicate key mappings:\n");
68                         errs++;
69                         fprintf(stderr, "  %s mapped to both %s and %s\n",
70                                         scratch, keyNames[used[ch]-1], keyNames[k]);
71                 }
72                 used[ch] = k + 1;
73         }
74         if (errs)
75                 exit(1);
76 }
77
78 ExtFunc int StartNewPiece(int scr, Shape *shape)
79 {
80         curShape[scr] = shape;
81         curY[scr] = boardVisible[scr] + 4;
82         curX[scr] = boardWidth[scr] / 2;
83         while (!ShapeVisible(shape, scr, curY[scr], curX[scr]))
84                 --curY[scr];
85         if (!ShapeFits(shape, scr, curY[scr], curX[scr]))
86                 return 0;
87         PlotShape(shape, scr, curY[scr], curX[scr], 1);
88         return 1;
89 }
90
91 ExtFunc void OneGame(int scr, int scr2)
92 {
93         MyEvent event;
94         int linesCleared, changed = 0;
95         int spied = 0, spying = 0, dropMode = 0;
96         int oldPaused = 0, paused = 0, pausedByMe = 0, pausedByThem = 0;
97         long pauseTimeLeft;
98         int pieceCount = 0;
99         int key;
100         char *p, *cmd;
101
102         speed = stepDownInterval;
103         ResetBaseTime();
104         InitBoard(scr);
105         if (scr2 >= 0) {
106                 spied = 1;
107                 spying = 1;
108                 InitBoard(scr2);
109                 UpdateOpponentDisplay();
110         }
111         ShowDisplayInfo();
112         SetITimer(speed, speed);
113         if (robotEnable) {
114                 RobotCmd(0, "GameType %s\n", gameNames[game]);
115                 RobotCmd(0, "BoardSize 0 %d %d\n",
116                                 boardVisible[scr], boardWidth[scr]);
117                 if (scr2 >= 0) {
118                         RobotCmd(0, "BoardSize 1 %d %d\n",
119                                         boardVisible[scr2], boardWidth[scr2]);
120                         RobotCmd(0, "Opponent 1 %s %s\n", opponentName, opponentHost);
121                         if (opponentFlags & SCF_usingRobot)
122                                 RobotCmd(0, "OpponentFlag 1 robot\n");
123                         if (opponentFlags & SCF_fairRobot)
124                                 RobotCmd(0, "OpponentFlag 1 fairRobot\n");
125                 }
126                 RobotCmd(0, "TickLength %.3f\n", speed / 1.0e6);
127                 RobotCmd(0, "BeginGame\n");
128                 RobotTimeStamp();
129         }
130         while (StartNewPiece(scr, ChooseOption(stdOptions))) {
131                 if (robotEnable && !fairRobot)
132                         RobotCmd(1, "NewPiece %d\n", ++pieceCount);
133                 if (spied) {
134                         short shapeNum;
135                         netint2 data[1];
136
137                         shapeNum = ShapeToNetNum(curShape[scr]);
138                         data[0] = hton2(shapeNum);
139                         SendPacket(NP_newPiece, sizeof(data), data);
140                 }
141                 for (;;) {
142                         changed = RefreshBoard(scr) || changed;
143                         if (spying)
144                                 changed = RefreshBoard(scr2) || changed;
145                         if (changed) {
146                                 RefreshScreen();
147                                 changed = 0;
148                         }
149                         CheckNetConn();
150                         switch (WaitMyEvent(&event, EM_any)) {
151                                 case E_alarm:
152                                         if (!MovePiece(scr, -1, 0))
153                                                 goto nextPiece;
154                                         else if (spied)
155                                                 SendPacket(NP_down, 0, NULL);
156                                         break;
157                                 case E_key:
158                                         p = strchr(keyTable, tolower(event.u.key));
159                                         key = p - keyTable;
160                                         if (robotEnable) {
161                                                 RobotCmd(1, "UserKey %d %s\n",
162                                                                 (int)(unsigned char)event.u.key,
163                                                                 p ? keyNames[key] : "?");
164                                                 break;
165                                         }
166                                         if (!p)
167                                                 break;
168                                 keyEvent:
169                                         if (paused && (key != KT_pause) && (key != KT_redraw))
170                                                 break;
171                                         switch(key) {
172                                                 case KT_left:
173                                                         if (MovePiece(scr, 0, -1) && spied)
174                                                                 SendPacket(NP_left, 0, NULL);
175                                                         break;
176                                                 case KT_right:
177                                                         if (MovePiece(scr, 0, 1) && spied)
178                                                                 SendPacket(NP_right, 0, NULL);
179                                                         break;
180                                                 case KT_rotate:
181                                                         if (RotatePiece(scr) && spied)
182                                                                 SendPacket(NP_rotate, 0, NULL);
183                                                         break;
184                                                 case KT_down:
185                                                         if (MovePiece(scr, -1, 0) && spied)
186                                                                 SendPacket(NP_down, 0, NULL);
187                                                         break;
188                                                 case KT_toggleSpy:
189                                                         spying = (!spying) && (scr2 >= 0);
190                                                         break;
191                                                 case KT_drop:
192                                                         if (DropPiece(scr) > 0) {
193                                                                 if (spied)
194                                                                         SendPacket(NP_drop, 0, NULL);
195                                                                 SetITimer(speed, speed);
196                                                         }
197                                                         dropMode = dropModeEnable;
198                                                         break;
199                                                 case KT_pause:
200                                                         pausedByMe = !pausedByMe;
201                                                         if (game == GT_classicTwo) {
202                                                                 netint2 data[1];
203
204                                                                 data[0] = hton2(pausedByMe);
205                                                                 SendPacket(NP_pause, sizeof(data), data);
206                                                         }
207                                                         paused = pausedByMe || pausedByThem;
208                                                         if (robotEnable)
209                                                                 RobotCmd(1, "Pause %d %d\n", pausedByMe,
210                                                                         pausedByThem);
211                                                         ShowPause(pausedByMe, pausedByThem);
212                                                         changed = 1;
213                                                         break;
214                                                 case KT_faster:
215                                                         if (game != GT_onePlayer)
216                                                                 break;
217                                                         speed = speed * 0.8;
218                                                         SetITimer(speed, SetITimer(0, 0));
219                                                         ShowDisplayInfo();
220                                                         changed = 1;
221                                                         break;
222                                                 case KT_redraw:
223                                                         ScheduleFullRedraw();
224                                                         if (paused)
225                                                                 RefreshScreen();
226                                                         break;
227                                         }
228                                         if (dropMode && DropPiece(scr) > 0) {
229                                                 if (spied)
230                                                         SendPacket(NP_drop, 0, NULL);
231                                                 SetITimer(speed, speed);
232                                         }
233                                         break;
234                                 case E_robot:
235                                 {
236                                         int num;
237
238                                         cmd = event.u.robot.data;
239                                         if ((p = strchr(cmd, ' ')))
240                                                 *p++ = 0;
241                                         else
242                                                 p = cmd + strlen(cmd);
243                                         for (key = 0; keyNames[key]; ++key)
244                                                 if (!strcmp(keyNames[key], cmd) &&
245                                                                 (fairRobot || (1 == sscanf(p, "%d", &num) &&
246                                                                         num == pieceCount)))
247                                                         goto keyEvent;
248                                         if (!strcmp(cmd, "Message")) {
249                                                 Message(p);
250                                                 changed = 1;
251                                         }
252                                         break;
253                                 }
254                                 case E_net:
255                                         switch(event.u.net.type) {
256                                                 case NP_giveJunk:
257                                                 {
258                                                         netint2 data[2];
259                                                         short column;
260
261                                                         memcpy(data, event.u.net.data, sizeof(data[0]));
262                                                         column = Random(0, boardWidth[scr]);
263                                                         data[1] = hton2(column);
264                                                         InsertJunk(scr, ntoh2(data[0]), column);
265                                                         if (spied)
266                                                                 SendPacket(NP_insertJunk, sizeof(data), data);
267                                                         break;
268                                                 }
269                                                 case NP_newPiece:
270                                                 {
271                                                         short shapeNum;
272                                                         netint2 data[1];
273
274                                                         FreezePiece(scr2);
275                                                         memcpy(data, event.u.net.data, sizeof(data));
276                                                         shapeNum = ntoh2(data[0]);
277                                                         StartNewPiece(scr2, NetNumToShape(shapeNum));
278                                                         break;
279                                                 }
280                                                 case NP_down:
281                                                         MovePiece(scr2, -1, 0);
282                                                         break;
283                                                 case NP_left:
284                                                         MovePiece(scr2, 0, -1);
285                                                         break;
286                                                 case NP_right:
287                                                         MovePiece(scr2, 0, 1);
288                                                         break;
289                                                 case NP_rotate:
290                                                         RotatePiece(scr2);
291                                                         break;
292                                                 case NP_drop:
293                                                         DropPiece(scr2);
294                                                         break;
295                                                 case NP_clear:
296                                                         ClearFullLines(scr2);
297                                                         break;
298                                                 case NP_insertJunk:
299                                                 {
300                                                         netint2 data[2];
301
302                                                         memcpy(data, event.u.net.data, sizeof(data));
303                                                         InsertJunk(scr2, ntoh2(data[0]), ntoh2(data[1]));
304                                                         break;
305                                                 }
306                                                 case NP_pause:
307                                                 {
308                                                         netint2 data[1];
309
310                                                         memcpy(data, event.u.net.data, sizeof(data));
311                                                         pausedByThem = ntoh2(data[0]);
312                                                         paused = pausedByMe || pausedByThem;
313                                                         if (robotEnable)
314                                                                 RobotCmd(1, "Pause %d %d\n", pausedByMe,
315                                                                         pausedByThem);
316                                                         ShowPause(pausedByMe, pausedByThem);
317                                                         changed = 1;
318                                                         break;
319                                                 }
320                                                 default:
321                                                         break;
322                                         }
323                                         break;
324                                 case E_lostRobot:
325                                 case E_lostConn:
326                                         goto gameOver;
327                                 default:
328                                         break;
329                         }
330                         if (paused != oldPaused) {
331                                 if (paused)
332                                         pauseTimeLeft = SetITimer(0, 0);
333                                 else
334                                         SetITimer(speed, pauseTimeLeft);
335                                 oldPaused = paused;
336                         }
337                 }
338         nextPiece:
339                 dropMode = 0;
340                 FreezePiece(scr);
341                 linesCleared = ClearFullLines(scr);
342                 if (linesCleared > 0 && spied)
343                         SendPacket(NP_clear, 0, NULL);
344                 if (game == GT_classicTwo && linesCleared > 1) {
345                         short junkLines;
346                         netint2 data[1];
347
348                         junkLines = linesCleared - (linesCleared < 4);
349                         data[0] = hton2(junkLines);
350                         SendPacket(NP_giveJunk, sizeof(data), data);
351                 }
352         }
353 gameOver:
354         SetITimer(0, 0);
355 }
356
357 ExtFunc int main(int argc, char **argv)
358 {
359         int initConn = 0, waitConn = 0, ch;
360         char *hostStr = NULL, *portStr = NULL;
361
362         standoutEnable = 1;
363         stepDownInterval = DEFAULT_INTERVAL;
364         MapKeys(DEFAULT_KEYS);
365         while ((ch = getopt(argc, argv, "hHRs:r:Fk:c:woDSp:i:")) != -1)
366                 switch (ch) {
367                         case 'c':
368                                 initConn = 1;
369                                 hostStr = optarg;
370                                 break;
371                         case 'w':
372                                 waitConn = 1;
373                                 break;
374                         case 'p':
375                                 portStr = optarg;
376                                 break;
377                         case 'i':
378                                 stepDownInterval = atof(optarg) * 1e6;
379                                 break;
380                         case 's':
381                                 initSeed = atoi(optarg);
382                                 myFlags |= SCF_setSeed;
383                                 break;
384                         case 'r':
385                                 robotEnable = 1;
386                                 robotProg = optarg;
387                                 myFlags |= SCF_usingRobot;
388                                 break;
389                         case 'F':
390                                 fairRobot = 1;
391                                 myFlags |= SCF_fairRobot;
392                                 break;
393                         case 'D':
394                                 dropModeEnable = 1;
395                                 break;
396                         case 'S':
397                                 standoutEnable = 0;
398                                 break;
399                         case 'k':
400                                 MapKeys(optarg);
401                                 break;
402                         case 'H':
403                                 DistInfo();
404                                 exit(0);
405                         case 'R':
406                                 Rules();
407                                 exit(0);
408                         case 'h':
409                                 Usage();
410                                 exit(0);
411                         default:
412                                 Usage();
413                                 exit(1);
414                 }
415         if (optind < argc || (initConn && waitConn)) {
416                 Usage();
417                 exit(1);
418         }
419         if (fairRobot && !robotEnable)
420                 fatal("You can't use the -F option without the -r option");
421         InitUtil();
422         if (robotEnable)
423                 InitRobot(robotProg);
424         InitNet();
425         InitScreens();
426         if (initConn || waitConn) {
427                 MyEvent event;
428
429                 game = GT_classicTwo;
430                 if (initConn)
431                         InitiateConnection(hostStr, portStr);
432                 else if (waitConn)
433                         WaitForConnection(portStr);
434                 {
435                         netint4 data[2];
436                         int major;
437
438                         data[0] = hton4(MAJOR_VERSION);
439                         data[1] = hton4(PROTOCOL_VERSION);
440                         SendPacket(NP_version, sizeof(data), data);
441                         if (WaitMyEvent(&event, EM_net) != E_net)
442                                 fatal("Network negotiation failed");
443                         memcpy(data, event.u.net.data, sizeof(data));
444                         major = ntoh4(data[0]);
445                         protocolVersion = ntoh4(data[1]);
446                         if (event.u.net.type != NP_version || major < MAJOR_VERSION)
447                                 fatal("Your opponent is using an old, incompatible version\n"
448                                         "of Netris.  They should get the latest version.");
449                         if (major > MAJOR_VERSION)
450                                 fatal("Your opponent is using an newer, incompatible version\n"
451                                         "of Netris.  Get the latest version.");
452                         if (protocolVersion > PROTOCOL_VERSION)
453                                 protocolVersion = PROTOCOL_VERSION;
454                 }
455                 if (protocolVersion < 3 && stepDownInterval != DEFAULT_INTERVAL)
456                         fatal("Your opponent's version of Netris predates the -i option.\n"
457                                         "For fairness, you shouldn't use the -i option either.");
458                 {
459                         netint4 data[3];
460                         int len;
461                         int seed;
462
463                         if (protocolVersion >= 3)
464                                 len = sizeof(data);
465                         else
466                                 len = sizeof(netint4[2]);
467                         if ((myFlags & SCF_setSeed))
468                                 seed = initSeed;
469                         else
470                                 seed = time(0);
471                         if (waitConn)
472                                 SRandom(seed);
473                         data[0] = hton4(myFlags);
474                         data[1] = hton4(seed);
475                         data[2] = hton4(stepDownInterval);
476                         SendPacket(NP_startConn, len, data);
477                         if (WaitMyEvent(&event, EM_net) != E_net ||
478                                         event.u.net.type != NP_startConn)
479                                 fatal("Network negotiation failed");
480                         memcpy(data, event.u.net.data, len);
481                         opponentFlags = ntoh4(data[0]);
482                         seed = ntoh4(data[1]);
483                         if (initConn) {
484                                 if ((opponentFlags & SCF_setSeed) != (myFlags & SCF_setSeed))
485                                         fatal("If one player sets the random number seed, "
486                                                         "both must.");
487                                 if ((myFlags & SCF_setSeed) && seed != initSeed)
488                                         fatal("Both players have set the random number seed, "
489                                                         "and they are unequal.");
490                                 if (protocolVersion >= 3 && stepDownInterval != ntoh4(data[2]))
491                                         fatal("Your opponent is using a different step-down "
492                                                 "interval (-i).\nYou must both use the same one.");
493                                 SRandom(seed);
494                         }
495                 }
496                 {
497                         char *userName;
498                         int len, i;
499
500                         userName = getenv("LOGNAME");
501                         if (!userName || !userName[0])
502                                 userName = getenv("USER");
503                         if (!userName || !userName[0])
504                                 strcpy(userName, "???");
505                         len = strlen(userName)+1;
506                         if (len > sizeof(opponentName))
507                                 len = sizeof(opponentName);
508                         SendPacket(NP_userName, len, userName);
509                         if (WaitMyEvent(&event, EM_net) != E_net ||
510                                         event.u.net.type != NP_userName)
511                                 fatal("Network negotiation failed");
512                         strncpy(opponentName, event.u.net.data,
513                                 sizeof(opponentName)-1);
514                         opponentName[sizeof(opponentName)-1] = 0;
515                         for (i = 0; opponentName[i]; ++i)
516                                 if (!isprint(opponentName[i]))
517                                         opponentName[i] = '?';
518                         for (i = 0; opponentHost[i]; ++i)
519                                 if (!isprint(opponentHost[i]))
520                                         opponentHost[i] = '?';
521                 }
522                 OneGame(0, 1);
523         }
524         else {
525                 game = GT_onePlayer;
526                 OneGame(0, -1);
527         }
528         return 0;
529 }
530
531 /*
532  * vi: ts=4 ai
533  * vim: noai si
534  */