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