samegame.pp 16 KB

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