samegame.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. {
  2. $Id$
  3. This program is both available in XTDFPC as in the FPC demoes.
  4. Copyright (C) 1999 by Marco van de Voort
  5. SameGame is a standard game in GNOME and KDE. I liked it, and I
  6. automatically brainstormed how I would implement it.
  7. It turned out to be really easy, and is basically only 100 lines or so,
  8. the rest is scorekeeping, helptext, menu etc.
  9. The game demonstrates some features of the MSMOUSE unit, and some of
  10. the Crt and Graph units. (depending whether it is compiled with
  11. UseGraphics or not)
  12. See the file COPYING.FPC, included in this distribution,
  13. for details about the copyright.
  14. This program is distributed in the hope that it will be useful,
  15. but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. **********************************************************************}
  18. PROGRAM SameGame;
  19. Uses Crt,Dos,
  20. {$IFDEF UseGraphics}
  21. Graph,
  22. {$ENDIF}
  23. GameUnit;
  24. CONST
  25. GrFieldX = 10; {X topleft of playfield}
  26. GrFieldY = 70; {Y topleft of playfield}
  27. ScalerX = 22; {ScalerX x Scaler y dots
  28. must be approx a square}
  29. ScalerY = 20;
  30. FieldX = 10; {Top left playfield
  31. coordinates in squares(textmode)}
  32. FieldY = 3; {Top left playfield coordinates}
  33. PlayFieldXDimension = 20; {Dimensions of playfield}
  34. PlayFieldYDimension = 15;
  35. RowDispl = 15;
  36. MenuX = 480;
  37. MenuY = 120;
  38. grNewGameLine = 'NEW GAME';
  39. grHelpLine = 'HELP';
  40. grEndGame = 'END GAME';
  41. {Used colors. Colors[0..2] are the colors used on the playfield, Colors[3]
  42. is the background and Colors[4] is the color used to mark the pieces}
  43. Colors : ARRAY [0..4] OF LONGINT = (White,Blue,Red,Black,LightMagenta);
  44. TYPE PlayFieldType=ARRAY[0..PlayFieldXDimension-1,0..PlayFieldYDimension-1] OF BYTE;
  45. {$IFDEF UseGraphics}
  46. PROCEDURE DisplayPlayField(CONST PlayField:PlayFieldType);
  47. {Screen routine, simply puts the array Playfield on screen.
  48. Both used for displaying the normal grid as the grid with a certain area marked}
  49. VAR X,Y : LONGINT;
  50. LastOne,
  51. NumbLast : LONGINT;
  52. BEGIN
  53. HideMouse;
  54. FOR Y:=0 TO PlayFieldYDimension-1 DO
  55. BEGIN
  56. X:=0;
  57. REPEAT
  58. LastOne:=PlayField[X,Y];
  59. NumbLast:=X;
  60. WHILE (PlayField[X,Y]=LastOne) AND (X<(PlayFieldXDimension-1))DO
  61. INC(X);
  62. SetFillStyle(SolidFill,Colors[LastOne]);
  63. Bar(GrFieldX+NumbLast*ScalerX,GrFieldY+Y*ScalerY,GrFieldX+X*ScalerX-1,GrFieldY+(Y+1)*ScalerY-1);
  64. UNTIL X>=(PlayFieldXDimension-1);
  65. END;
  66. ShowMouse;
  67. END;
  68. {$ELSE}
  69. PROCEDURE DisplayPlayField(CONST PlayField:PlayFieldType);
  70. {Screen routine, simply puts the array Playfield on screen.
  71. Both used for displaying the normal grid as the grid with a certain area marked}
  72. VAR X,Y : LONGINT;
  73. BEGIN
  74. FOR Y:=0 TO PlayFieldYDimension-1 DO
  75. BEGIN
  76. GotoXY(FieldX,Y+FieldY);
  77. FOR X:=0 TO PlayFieldXDimension-1 DO
  78. BEGIN
  79. TextColor(Colors[PlayField[X,Y]]);
  80. Write(#219#219);
  81. END;
  82. END;
  83. END;
  84. {$ENDIF}
  85. PROCEDURE ShowHelp;
  86. {Shows some explanation of the game and waits for a key}
  87. {$ifndef UseGraphics}
  88. VAR I : LONGINT;
  89. {$endif}
  90. BEGIN
  91. {$IFDEF UseGraphics}
  92. HideMouse;
  93. SetbkColor(black);
  94. SetViewPort(0,0,getmaxx,getmaxy,clipoff);
  95. ClearViewPort;
  96. SetTextStyle(0,Horizdir,2);
  97. OutTextXY(220,10,'SAMEGAME');
  98. SetTextStyle(0,Horizdir,1);
  99. OutTextXY(5,40+1*LineDistY,' is a small game, with a principle copied from some KDE game');
  100. OutTextXY(5,40+3*LineDistY,'I tried to implement it under Go32V2 to demonstrate the MsMouse unit');
  101. OutTextXY(5,40+4*LineDistY,'When it worked, I tried to get it running under Linux. I succeeded,');
  102. OutTextXY(5,40+5*LineDistY,'but the mouse unit of the API doesn'#39't work with GPM 1.17');
  103. OutTextXY(5,40+7*LineDistY,'If you move over the playfield, aggregates of one color will be marked');
  104. OutTextXY(5,40+8*LineDistY,'in purple. If you then press the left mouse button, that aggregate will');
  105. OutTextXY(5,40+9*LineDistY,'disappear, and the playfield will collapse to the bottom-left. Please');
  106. OutTextXY(5,40+10*LineDistY,'keep in mind that only an aggregate of two blocks or more will disappear.');
  107. OutTextXY(5,40+12*LineDistY,'For every aggregate you let disappear you get points, but the score is');
  108. OutTextXY(5,40+13*LineDistY,'quadratic proportional to the number of blocks killed. So 4 times killing');
  109. OutTextXY(5,40+14*LineDistY,' a 4 block aggregate scores 4 points, and one time 16 blocks will score 64');
  110. OutTextXY(5,40+15*LineDistY,'blocks. The purpose of the game is obtaining the highscore');
  111. OutTextXY(5,40+17*LineDistY,'If you manage to empty the entire playfield, you'#39'll get a bonus');
  112. OutTextXY(5,40+19*LineDistY,'Press any key to get back to the game');
  113. ShowMouse;
  114. {$ELSE}
  115. FOR I:=2 TO 24 DO
  116. BEGIN
  117. GotoXY(1,I);
  118. ClrEol;
  119. END;
  120. GotoXY(1,3); TextColor(White);
  121. Write('SAMEGAME');
  122. SetDefaultColor;
  123. WriteLn(' is a small game, with a principle copied from some KDE game');
  124. WriteLn;
  125. WriteLn('I tried to implement it under Go32V2 to demonstrate the MsMouse unit');
  126. Writeln('When it worked, I tried to get it running under Linux. I succeeded,');
  127. Writeln('but the mouse unit of the API doesn'#39't work with GPM 1.17');
  128. Writeln;
  129. WriteLn('If you move over the playfield, aggregates of one color will be marked');
  130. Writeln('in purple. If you then press the left mouse button, that aggregate will');
  131. Writeln('disappear, and the playfield will collapse to the bottom-left. Please');
  132. Writeln('keep in mind that only an aggregate of two blocks or more will disappear.');
  133. Writeln;
  134. Writeln('For every aggregate you let disappear you get points, but the score is');
  135. Writeln('quadratic proportional to the number of blocks killed. So 4 times killing');
  136. Writeln(' a 4 block aggregate scores 4 points, and one time 16 blocks will score 64');
  137. Writeln('blocks. The purpose of the game is obtaining the highscore');
  138. Writeln;
  139. Writeln('If you manage to empty the entire playfield, you'#39'll get a bonus');
  140. Writeln;
  141. WriteLn('Press any key to get back to the game');
  142. {$ENDIF}
  143. GetKey;
  144. END;
  145. VAR MarkField,PlayField : PlayFieldType; {The playfield, and the marked form}
  146. CubesMarked : LONGINT; {Cubes currently marked}
  147. Score : LONGINT; {The current score}
  148. LastScore : LONGINT;
  149. PROCEDURE ShowButtons;
  150. {Shows the clickable buttons}
  151. BEGIN
  152. {$IFNDEF UseGraphics}
  153. TextColor(Yellow); TextBackGround(Blue);
  154. GotoXY(60,5); Write('NEW game');
  155. GotoXY(60,6); Write('HELP');
  156. GotoXY(60,7); Write('END game');
  157. {$IFDEF Linux}
  158. GotoXY(60,8); Write('Force IBM charset');
  159. {$ENDIF}
  160. SetDefaultColor;
  161. {$ELSE}
  162. SetTextStyle(0,Horizdir,1);
  163. OutTextXY(MenuX,MenuY,grNewGameLine);
  164. OutTextXY(MenuX,MenuY+RowDispl,grHelpLine);
  165. OutTextXY(MenuX,MenuY+2*RowDispl,grEndGame);
  166. {$ENDIF}
  167. END;
  168. FUNCTION PlayFieldPiecesLeft:LONGINT;
  169. {Counts pieces/cubes/blocks left on the playfield}
  170. VAR I,J,K : LONGINT;
  171. BEGIN
  172. K:=0;
  173. FOR I:=0 TO PlayFieldXDimension-1 DO
  174. FOR J:=0 TO PlayFieldYDimension-1 DO
  175. IF PlayField[I,J]<>3 THEN
  176. INC(K);
  177. PlayFieldPiecesLeft:=K;
  178. END;
  179. PROCEDURE ShowScore;
  180. {Simply procedure to update the score}
  181. {$IFDEF UseGraphics}
  182. VAR S : String;
  183. {$ENDIF}
  184. BEGIN
  185. {$IFDEF UseGraphics}
  186. Str(Score:5,S);
  187. SetFillStyle(SolidFill,0);
  188. Bar(300,440,450,458);
  189. OutTextXY(300,440,'Score :'+S);
  190. {$ELSE}
  191. TextColor(White);
  192. GotoXY(20,23); Write(' ':20);
  193. GotoXY(20,23); Write('Score : ',Score);
  194. SetDefaultColor;
  195. {$ENDIF}
  196. END;
  197. FUNCTION CubesToScore : LONGINT;
  198. {Function to calculate score from the number of cubes. Should have a higher
  199. order than linear, or the purpose of the game disappears}
  200. BEGIN
  201. CubesToScore:=(CubesMarked*CubesMarked) DIV 4;
  202. END;
  203. PROCEDURE MarkAfield(X,Y:LONGINT);
  204. {Recursively marks the area adjacent to (X,Y);}
  205. VAR TargetColor : LONGINT;
  206. PROCEDURE MarkRecur(X1,Y1:LONGINT);
  207. {Marks X1,Y1, checks if neighbours (horizontally or vertically) are the
  208. same color}
  209. BEGIN
  210. IF (PlayField[X1,Y1]=TargetColor) AND (MarkField[X1,Y1]<>4) THEN
  211. BEGIN
  212. MarkField[X1,Y1]:=4;
  213. INC(CubesMarked);
  214. IF X1>0 THEN
  215. MarkRecur(X1-1,Y1);
  216. IF Y1>0 THEN
  217. MarkRecur(X1,Y1-1);
  218. IF X1<(PlayFieldXDimension-1) THEN
  219. MarkRecur(X1+1,Y1);
  220. IF Y1<(PlayFieldYDimension-1) THEN
  221. MarkRecur(X1,Y1+1);
  222. END;
  223. END;
  224. BEGIN
  225. CubesMarked:=0;
  226. TargetColor:=PlayField[X,Y];
  227. IF TargetColor<>3 THEN {Can't mark black space}
  228. MarkRecur(X,Y);
  229. END;
  230. PROCEDURE FillPlayfield;
  231. {Initial version, probably not nice to play with.
  232. Some Life'ish algoritm would be better I think. (so that more aggregates exist)}
  233. VAR X,Y,Last,Now : LONGINT;
  234. BEGIN
  235. Last:=0;
  236. FOR X:=0 TO PlayFieldXDimension-1 DO
  237. FOR Y:=0 TO PlayFieldYDimension-1 DO
  238. BEGIN
  239. Now:=RANDOM(4);
  240. IF Now=3 THEN
  241. Now:=Last;
  242. PlayField[X,Y]:=Now;
  243. Last:=Now;
  244. END;
  245. MarkField:=PlayField;
  246. END;
  247. PROCEDURE Colapse;
  248. {Processes the playfield if the mouse button is used.
  249. First the procedure deletes the marked area, and let gravity do its work
  250. Second the procedure uses as if some gravity existed on the left of the
  251. playfield }
  252. VAR X, Y,J :LONGINT;
  253. BEGIN
  254. {Vertical colapse: All marked pieces are deleted, and let gravity do it's work}
  255. IF CubesMarked>1 THEN
  256. BEGIN
  257. FOR X:=0 TO PlayFieldXDimension-1 DO
  258. BEGIN
  259. Y:=PlayFieldYDimension-1; J:=Y;
  260. REPEAT
  261. IF MarkField[X,Y]<>4 THEN
  262. BEGIN
  263. PlayField[X,J]:=PlayField[X,Y];
  264. DEC(J);
  265. END;
  266. DEC(Y);
  267. UNTIL Y<0;
  268. FOR Y:=0 TO J DO
  269. PlayField[X,Y]:=3;
  270. END;
  271. J:=0;
  272. FOR X:=PlayFieldXDimension-2 DOWNTO 0 DO
  273. BEGIN
  274. IF PlayfIeld[X,PlayFieldYDimension-1]=3 THEN
  275. BEGIN
  276. Move(PlayfIeld[X+1,0],PlayField[X,0],PlayFieldYDimension*(PlayFieldXDimension-X-1));
  277. INC(J);
  278. END;
  279. END;
  280. IF J<>0 THEN
  281. FillChar(PlayField[PlayFieldXDimension-J,0],J*PlayFieldYDimension,#3);
  282. INC(Score,CubesToScore);
  283. ShowScore;
  284. END;
  285. END;
  286. PROCEDURE BuildScreen;
  287. {Some procedures that build the screen}
  288. BEGIN
  289. {$IFDEF UseGraphics}
  290. setbkcolor(black);
  291. setviewport(0,0,getmaxx,getmaxy,clipoff);
  292. clearviewport;
  293. {$ELSE}
  294. ClrScr;
  295. {$ENDIF}
  296. Score:=0;
  297. ShowScore;
  298. ShowButtons;
  299. ShowHighScore;
  300. ShowMouse;
  301. {$IFDEF UseGraphics}
  302. SetTextStyle(0,Horizdir,2);
  303. OuttextXY(10,10,'SameGame v0.03, (C) by Marco v/d Voort. ');
  304. SetTextStyle(0,Horizdir,1);
  305. OuttextXY(50,40,'A demo for the FPC RTL and API units Crt,(MS)Mouse and Graph');
  306. {$ELSE}
  307. GotoXY(1,1);
  308. TextColor(Yellow);
  309. Write('SameGame v0.02');
  310. TextColor(White);
  311. Write(' A demo for the ');
  312. TextColor(Yellow); Write('FPC');
  313. TextColor(White); Write(' API or MsMouse unit. By Marco van de Voort');
  314. SetDefaultColor;
  315. {$ENDIF}
  316. IF LastScore<>0 THEN
  317. BEGIN
  318. GotoXY(10,20);
  319. Write('The score in the last game was :',LastScore);
  320. END;
  321. DisplayPlayField(PlayField);
  322. MarkField:=PlayField;
  323. END;
  324. PROCEDURE DoMainLoopMouse;
  325. {The main game loop. The entire game runs in this procedure, the rest is
  326. initialisation/finalisation (like loading and saving highscores etc etc)}
  327. VAR X,Y,
  328. MX,MY,MState,Dummy : LONGINT;
  329. EndOfGame : LONGINT;
  330. S : String;
  331. BEGIN
  332. RANDOMIZE;
  333. REPEAT
  334. FillPlayField;
  335. BuildScreen;
  336. EndOfGame:=0;
  337. REPEAT
  338. GetMouseState(MX,MY,MState);
  339. {$IFDEF UseGraphics}
  340. X:=2*((MX-GrFieldX) DIV ScalerX) +FieldX;
  341. Y:=((MY-GrFieldY) DIV ScalerY) +FieldY-1;
  342. {$ELSE}
  343. X:=MX SHR 3;
  344. Y:=MY SHR 3;
  345. {$ENDIF}
  346. IF PlayFieldPiecesLeft=0 THEN
  347. BEGIN
  348. INC(Score,1000);
  349. EndOfGame:=1;
  350. END
  351. ELSE
  352. BEGIN
  353. {$IFDEF UseGraphics}
  354. IF (MX>=MenuX) AND (MX<(MenuX+16*Length(GrNewGameLine))) THEN
  355. BEGIN {X in clickable area}
  356. IF (MY>=MenuY) AND (MY<(MenuY+RowDispl*3+2)) THEN
  357. BEGIN
  358. X:=65; {X doesn't matter as long as it is 60..69}
  359. Y:=((MY-MenuY) DIV RowDispl)+4;
  360. END;
  361. END;
  362. {$ENDIF}
  363. IF (X>=60) AND (X<=69) THEN
  364. BEGIN
  365. IF (MState AND LButton) <>0 THEN {If leftbutton pressed,}
  366. BEGIN
  367. IF Y=4 THEN
  368. EndOfGame:=1;
  369. IF Y=6 THEN
  370. EndOfGame:=2;
  371. IF (EndOfGame>0) AND (PlayFieldPiecesLeft=0) THEN
  372. INC(Score,1000);
  373. IF Y=5 THEN
  374. BEGIN
  375. ShowHelp;
  376. BuildScreen;
  377. END;
  378. {$IFDEF Linux}
  379. IF Y=7 THEN
  380. BEGIN
  381. write(#27+'(K');
  382. BuildScreen;
  383. END;
  384. {$ENDIF}
  385. END;
  386. END;
  387. IF (X>=(FieldX-2)) AND (Y>=(FieldY-2)) THEN
  388. BEGIN
  389. DEC(X,FieldX-1);
  390. DEC(Y,FieldY-1);
  391. X:=X SHR 1;
  392. IF (X<PlayFieldXDimension) AND (Y<PlayFieldYDimension) THEN
  393. BEGIN
  394. IF MarkField[X,Y]<>4 THEN
  395. BEGIN
  396. MarkField:=PlayField;
  397. MarkAfield(X,Y);
  398. DisplayPlayField(MarkField);
  399. TextColor(White);
  400. GotoXY(20,22);
  401. Write(' ':20);
  402. GotoXY(20,22);
  403. Write('Marked :',CubesToScore);
  404. END;
  405. IF (MarkField[X,Y]=4) AND ((MState AND LButton) <>0) THEN
  406. {If leftbutton pressed,}
  407. BEGIN
  408. REPEAT {wait untill it's released.
  409. The moment of pressing counts}
  410. GetMouseState(X,Y,Dummy);
  411. UNTIL (Dummy AND LButton)=0;
  412. Colapse;
  413. MarkField:=PlayField;
  414. DisplayPlayField(MarkField);
  415. END
  416. END
  417. END;
  418. IF KeyPressed THEN
  419. BEGIN
  420. X:=GetKey;
  421. IF (CHR(X) IN ['X','x','Q','q']) OR (X=27) THEN
  422. EndOfGame:=2;
  423. END;
  424. END;
  425. UNTIL EndOfGame>0;
  426. ShowScore;
  427. X:=SlipInScore(Score);
  428. IF X<>0 THEN
  429. BEGIN
  430. HideMouse;
  431. ShowHighScore;
  432. {$IFDEF UseGraphics}
  433. Str(Score:5,S);
  434. OutTextXY(HighX+150,HighY+LineDistY*(10-X),S);
  435. GrInputStr(S,HighX,HighY+LineDistY*(10-X),16,12,10,FALSE,AlfaBeta);
  436. {$ELSE}
  437. InputStr(S,HighX,HighY+12-X,10,FALSE,AlfaBeta);
  438. {$ENDIF}
  439. HighScore[X-1].Name:=S;
  440. ShowMouse;
  441. END;
  442. LastScore:=Score;
  443. UNTIL EndOFGame=2;
  444. END;
  445. CONST FileName='samegame.scr';
  446. VAR I : LONGINT;
  447. {$IFDEF UseGraphics}
  448. gd,gm : INTEGER;
  449. Pal : PaletteType;
  450. {$ENDIF}
  451. BEGIN
  452. {$IFDEF UseGraphics}
  453. gm:=vgahi;
  454. gd:=vga;
  455. InitGraph(gd,gm,'');
  456. if GraphResult <> grOk then
  457. begin
  458. Writeln('Graph driver ',gd,' graph mode ',gm,' not supported');
  459. Halt(1);
  460. end;
  461. SetFillStyle(SolidFill,1);
  462. GetDefaultPalette(Pal);
  463. SetAllPalette(Pal);
  464. {$ENDIF}
  465. IF NOT MousePresent THEN
  466. BEGIN
  467. Writeln('No mouse found. A mouse is required!');
  468. HALT;
  469. END;
  470. FOR I:=1 TO 10 DO
  471. HighScore[I].Score:=I*1500;
  472. LoadHighScore(FileName);
  473. InitMouse;
  474. CursorOff;
  475. {$IFDEF UseGraphics}
  476. HighX:=450; HighY:=220; {the position of the highscore table}
  477. {$else}
  478. HighX:=52; HighY:=10; {the position of the highscore table}
  479. {$endif}
  480. DoMainLoopMouse;
  481. HideMouse;
  482. DoneMouse;
  483. CursorOn;
  484. SaveHighScore;
  485. {$IFDEF UseGraphics}
  486. CloseGraph;
  487. {$ENDIF}
  488. ClrScr;
  489. Writeln;
  490. Writeln('Last games'#39' score was : ',Score);
  491. END.
  492. {
  493. $Log$
  494. Revision 1.4 2000-01-21 00:44:51 peter
  495. * remove unused vars
  496. * renamed to .pp
  497. Revision 1.3 1999/12/31 17:04:22 marco
  498. Graphical version
  499. Revision 1.2 1999/06/01 19:24:33 peter
  500. * updates from marco
  501. Revision 1.1 1999/05/27 21:36:34 peter
  502. * new demo's
  503. * fixed mandel for linux
  504. }