samegame.pp 15 KB

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