See: Description
Package | Description |
---|---|
org.gcs.robot |
RobotChase is a game in which the player moves about on a rectangular grid, while trying to escape advancing robots. The player accumulates points by killing robots and advancing to the next level. Robots die when they collide with each other or with one of several obstructions. The project is hosted on SourceForge.
Download: Click here to download the latest jar or Mac application files.
The game was implemented in UCSD Pascal in the early eighties, as seen in Listing 1. The original may be found on Volume 1 of the USUS Software Library hosted in the west wing of the Jefferson Computer Museum.
FeaturesAlthough based on the classic version, the game includes several modern features:
The design of RobotChase is an example of the Model-View-Controller pattern. The main class (RobotChase
) instantiates a model of the game (RCModel
) and three views of that model (RCView
, RCStatus
, and RCInfo
).
The interaction between the model and its views uses the Observer pattern. The model extends the class Observable
, and each of the views implements the Observer
interface. In this way, each view can update itself whenever the model signals a change in state.
RCView
, in turn, delegates the drawing of each game tile to RCTile
. The class RCImage
is a factory that supplies one of several sets of images to RCView
. The class RCPrefs
provides static methods that maintain a persistent user state. RCHelp
is a modal dialog that displays game instructions.
The game is played with the keyboard or mouse. By implementing KeyListener
, the controller accepts the user's input and conducts game play. The main view implements MouseListener
and MouseMotionListener
as an alternate means of play.
The keys used to control the game are managed in the Key
enumeration. The modal dialog RCKeys
provides an interface for editing the corresponding key bindings.
Internally, the game model uses numeric virtual keycodes (96-105) to control movement. The numbers represent the eight (semi-) cardinal directions. The number five signifies no movement:
NW 7 | North 8 |
9 NE |
West 4 | 5 | 6 East |
SW 1 | 2 South |
3 SE |
0 Jump |
Implementation
The present implementation uses Java. The latest source code is available on SourceForge. The ant build target compiles the source and creates a jar file of the required classes and images. The jar file is then copied to a Mac application bundle. The file Info.plist
is edited and copied. Diagnostic output from the universalJavaApplicationStub
may be seen in the Console utility.
1.0.8 Migrate to universalJavaApplicationStub
; Java 1.6+.
1.0.7 Assorted bug fixes; new certificate.
1.0.6 Update logo; show window dimensions during resize.
1.0.5 Stop timer if won; sign jars; allow shift-tab navigation.
1.0.4 Improve help; leaner distribututions; fix mouse bug.
1.0.3 Animate player on mouse up; add editor for key bindings.
1.0.2 Resume previous game and screen size; preserve jumps.
1.0.1 Add jump convenience; move to SourceForge.
1.0.0 Add resize message; initial public release.
0.9.9 Refactor main view and tiles; normalize player position.
0.9.8 Normalize Observer pattern; handle minimum game board size.
0.9.7 Better mouse support; add high level; preserve game on resize.
0.9.6 Add mouse support; better on-line help.
0.9.5 Add preferences; normalize package name; third beta release.
0.9.4 Add high score; normalize layout usage; second beta release.
0.9.3 Initial beta release.
Listing 1.
PROGRAM CHASE;
USES APPLESTUFF;
CONST PLAYER = 'O'; {SYMBOL FOR THE PLAYER}
EDGE = 'I'; {SYMBOL FOR THE FENCE}
OBST = '*'; {SYMBOL FOR AN OBSTRUCTION}
ROBOT = 'R'; {SYMBOL FOR A ROBOT}
BLANK = ' '; {AN ASCII BLANK}
DROB = 3; {STARTING NO OF ROBOTS}
ROBMAX = 19; {MAX NO OF ROBOTS ALLOWED}
XMAX = 39; {MAX HORIZONTAL FIELD DIMENSION}
YMAX = 14; {MAX VERTICAL FIELD DIMENSION}
TOP = 2; {SPACE ABOVE FIELD}
SIDE = 5; {SPACE TO LEFT OF FIELD}
CLRSCRN = 12; {CLEAR SCREEN CODE}
VAR FIELD : PACKED ARRAY[0..XMAX,0..YMAX] OF CHAR;
AGAIN,PLAY : BOOLEAN;
WIN : BOOLEAN;
MI,MJ : INTEGER; {COORDINATES OF PLAYER}
R : INTEGER; {NUMBER OF ROBOTS LEFT}
RI,RJ : ARRAY[1..ROBMAX] OF INTEGER; {ROBOT COORDINATES}
DIFF : INTEGER; {DIFFICULTY}
IDIFF : 0..10; {INITIAL DIFFICULTY}
GAMENU : INTEGER; {GAME NUMBER}
M : CHAR;
NROB : INTEGER; {NUMBER OF ROBOTS}
WINS : INTEGER; {NUMBER OF GAMES WON}
GOODCHAR : SET OF CHAR; {GOOD CHARACTERS}
MOVES : INTEGER; {COUNT OF MOVES}
CRASH : INTEGER; {NO OF ROBOTS "CRASHED"}
FUNCTION RND(LO,HI:INTEGER):INTEGER; {CALL APPLESTUFF}
BEGIN
RND := LO + RANDOM MOD (HI - LO + 1);
END;
PROCEDURE DOMOVE(COL,ROW:INTEGER;SYMBOL:CHAR); {DISPLAY SYMBOL AT I,J}
BEGIN
GOTOXY(COL,ROW); {POSITION CURSOR}
WRITE(SYMBOL)
END; {END OF DOMOVE PROCEDURE}
PROCEDURE CLEARSCREEN; {FOR AN ADM3-A CHANGE IT FOR OTHER TERMINAL}
BEGIN
WRITE(CHR(CLRSCRN),CHR(0),CHR(0),CHR(0),CHR(0),CHR(0))
END;
PROCEDURE INSTRUCTIONS; {DISPLAY INSTRUCTIONS}
VAR M:CHAR;
BEGIN
CLEARSCREEN;
WRITELN('WELCOME TO THE EXCITING GAME OF CHASE');
GOTOXY(0,4);
WRITE('WOULD YOU LIKE INSTRUCTIONS ? (Y OR N) ');
READ(M);
IF M IN ['Y','y'] THEN
BEGIN
WRITELN;WRITELN;
WRITELN('INSTRUCTIONS:');
WRITELN('YOU,"O",ARE IN A HIGH VOLTAGE MAZE.');
WRITELN('THE ROBOT COMPUTERS,"R",ARE TRYING TO DESTROY YOU.');
WRITELN('TO WIN, YOU MUST DESTROY THE COMPUTERS.');
WRITELN('THIS IS DONE BY RUNNING THEM INTO FENCE POSTS,"*",');
WRITELN('OR BY RUNNING THEM INTO EACH OTHER.');
WRITELN('THE DIAGRAM BELOW THE MAZE SHOWS HOW YOU CAN MOVE');
WRITELN('THE ROBOTS WILL TRY TO FOLLOW YOU.');
WRITELN('THERE ARE 3 ROBOTS TO START FOR A BEGINNER.');
WRITELN('THE NUMBER WILL INCREASE AS YOU WIN GAMES !');
WRITELN;
WRITELN('GOOD LUCK!!!!!');
WriteLn; Write('Press any key: '); READ(M)
END
END; {END OF INSTRUCTIONS}
PROCEDURE STARTGAME;
VAR SK: CHAR;
BEGIN
WRITELN;WRITELN;WRITELN;
CLEARSCREEN;
WRITELN(' HOW GOOD A PLAYER ARE YOU ?');
WRITELN;
WRITELN(' BEGINNER - B');
WRITELN(' INTERMEDIATE - I');
WRITELN(' EXPERT - E');
WRITELN(' OLD PRO - P');
WRITELN;
WRITE(' TYPE IN YOUR SKILL ');
READ (SK);
WRITELN;
WHILE NOT (SK IN ['B','b','I','i','E','e','P','p']) DO
BEGIN
GOTOXY(10,10);
WRITE(' WHAT WAS THAT AGAIN PLEASE ? ',CHR(7));
READ (SK);
WRITELN
END;
CASE SK OF
'B','b': IDIFF:=0;
'I','i': IDIFF:=1;
'E','e': IDIFF:=3;
'P','p': IDIFF:=5;
END;
END;
PROCEDURE INITIALIZE; {SET UP BLANK FIELD SURROUNDED BY FENCE}
VAR I,J:INTEGER;
BEGIN
FOR I:=0 TO XMAX DO
BEGIN
FOR J:=0 TO YMAX DO
IF((I=0) OR (I=XMAX) OR (J=0) OR (J=YMAX)) THEN FIELD[I,J]:=EDGE
ELSE FIELD[I,J]:=BLANK
END;
END; {END OF INITIALIZE}
PROCEDURE INNERFIELD; {SET UP PLAYER, ROBOTS AND OBSTRUCTIONS}
VAR I,J,L,POSTS:INTEGER;
BEGIN
MI:=RND(1,XMAX-1); MJ:=RND(1,YMAX-1); {PLAYER AT A RANDOM POSITION}
FIELD[MI,MJ]:=PLAYER;
R:=NROB;
FOR L:=1 TO R DO {NOW DO R ROBOTS}
BEGIN
REPEAT
I:=RND(0,XMAX);J:=RND(0,YMAX);
UNTIL FIELD[I,J]=BLANK;
FIELD[I,J]:=ROBOT;
RI[L]:=I;
RJ[L]:=J
END;
POSTS:=RND(25,35); {NOW SET UP 25 TO 35 POSTS}
FOR L:=1 TO POSTS DO
BEGIN
REPEAT
IF DIFF>3 THEN
BEGIN
I:=RND(0,XMAX);
J:=RND(0,YMAX)
END ELSE
BEGIN
I:=RND(1,XMAX-1);
J:=RND(1,YMAX-1)
END;
UNTIL FIELD[I,J]=BLANK;
FIELD[I,J]:=OBST
END;
END; {END OF INNERFIELD}
PROCEDURE MAP; {DISPLAY PLAYING FIELD}
VAR I,J:INTEGER;
BEGIN
CLEARSCREEN;
WRITELN('GAME DIFF ROBOTS WINS MOVE':79);
WRITE(' ':44,GAMENU:3,DIFF:5,R:8,WINS:10,MOVES:8);
GOTOXY(0,0);
FOR J:=0 TO YMAX DO
BEGIN
FOR I:=0 TO XMAX DO WRITE(FIELD[I,J]);
WRITELN
END;
WRITELN;
WRITELN('7 8 9 Q = QUIT');
WRITELN('4 X 6 5 = NO MOVE');
WRITE( '1 2 3 MOVE => ');
END; {END OF MAP}
PROCEDURE MOVE; {ENTER YOUR MOVE FROM KEYBOARD}
VAR M : INTEGER;
C : CHAR;
BAD : BOOLEAN;
BEGIN
BAD:=FALSE;
REPEAT
WRITE(' ',CHR(8));
READ (C);
IF NOT (C IN GOODCHAR) THEN
BEGIN
GOTOXY(4,21);
BAD:=TRUE;
WRITE('BAD MOVE, PLEASE TRY AGAIN ':33,CHR(7))
END;
UNTIL (C IN GOODCHAR);
IF BAD THEN
BEGIN
GOTOXY(4,21);
WRITE(' ':40);
GOTOXY(10,22);
END;
IF C IN ['Q','q'] THEN
BEGIN
PLAY:=FALSE;
WIN:=FALSE
END;
M:=ORD(C)-48;
FIELD[MI,MJ]:=BLANK;
DOMOVE(MI,MJ,BLANK);
CASE M OF
1: BEGIN MI:=MI-1; MJ:=MJ+1 END;
2: MJ:=MJ+1;
3: BEGIN MI:=MI+1; MJ:=MJ+1 END;
4: MI:=MI-1;
5: ;
6: MI:=MI+1;
7: BEGIN MI:=MI-1; MJ:=MJ-1 END;
8: MJ:=MJ-1;
9: BEGIN MI:=MI+1; MJ:=MJ-1 END
END;
MOVES:=MOVES+1;
IF FIELD[MI,MJ] = BLANK THEN
BEGIN
DOMOVE(MI,MJ,PLAYER);
FIELD[MI,MJ]:=PLAYER
END ELSE
BEGIN
IF FIELD[MI,MJ] = EDGE THEN
BEGIN
WIN:=FALSE;
PLAY:=FALSE;
WRITELN('OUCH, YOU GOT ELECTROCUTED!')
END ELSE
BEGIN
IF FIELD[MI,MJ] = ROBOT THEN
WRITELN('THWACK! YOU RAN INTO A ROBOT (TURKEY!)') ELSE
WRITELN('ZZAP! YOU RAN INTO AN ELECTIFIED POST');
WIN:=FALSE;
PLAY:=FALSE
END;
END;
END; {END OF MOVE PROCEDURE}
PROCEDURE ROBOTMOVE; {COMPUTE MOVE FOR R OR FEWER ROBOTS}
VAR M,L,I,J:INTEGER;
BEGIN
FOR L:=1 TO NROB DO
BEGIN
IF((RI[L]<>0) AND (WIN)) THEN
BEGIN
FIELD[RI[L],RJ[L]]:=BLANK;
DOMOVE(RI[L],RJ[L],BLANK);
IF MI>RI[L] THEN RI[L]:=RI[L]+1;
IF MI<RI[L] THEN RI[L]:=RI[L]-1;
IF MJ>RJ[L] THEN RJ[L]:=RJ[L]+1;
IF MJ<RJ[L] THEN RJ[L]:=RJ[L]-1;
I:=RI[L];J:=RJ[L];
IF FIELD[I,J]=BLANK THEN
BEGIN
FIELD[RI[L],RJ[L]]:=ROBOT;
DOMOVE(I,J,ROBOT)
END
ELSE
BEGIN
IF ((FIELD[I,J]=OBST) OR (FIELD[I,J]=ROBOT)) THEN
BEGIN
GOTOXY(XMAX+8,CRASH+3);
CRASH:=CRASH+1;
WRITELN('CRASH!! ',R-1:2,' OF ',NROB,' ROBOTS REMAIN');
R:=R-1;
GOTOXY(53,1); {CHANGE NO OF ROBOTS}
WRITE(R:8);
RI[L]:=0;
IF R=0 THEN
BEGIN
GOTOXY(XMAX+8,CRASH+3);
{WRITELN('CONGRATULATIONS! GOOD WORK!');}
{GOTOXY(XMAX+8,CRASH+4);}
WRITELN('YOU HAVE DESTROYED THEM ALL!!!');
WIN:=TRUE;
PLAY:=FALSE
END;
END;
END;
IF FIELD[RI[L],RJ[L]]=FIELD[MI,MJ] THEN
BEGIN
WRITELN('ZAP! A ROBOT GOT YOU!!!');
WIN:=FALSE;
PLAY:=FALSE
END;
END;
END;
END; {END OF ROBOTMOVE PROCEDURE}
BEGIN {START OF MAIN PROGRAM}
RANDOMIZE;
GOODCHAR:=['1'..'9','Q','q'];
GAMENU:=1;
WINS:=0;
AGAIN:=TRUE;
PLAY:=TRUE; {INITIALIZE QUIT}
INSTRUCTIONS; {DISPLAY INSTRUCTIONS}
STARTGAME; {INPUT STARTING POSITION AND SKILL LEVEL}
DIFF:=IDIFF; {INITIAL DIFFICULTY LEVEL}
NROB:=DROB+DIFF*2; {INITIAL NUMBER OF ROBOTS}
WHILE AGAIN DO
BEGIN
MOVES:=1;WIN:=TRUE;CRASH:=0;
INITIALIZE; {CLEARS FIELD[X,Y]}
INNERFIELD; {SETS UP PLAYING FIELD}
WHILE PLAY DO
BEGIN
IF MOVES=1 THEN MAP ELSE
BEGIN
GOTOXY(70,1);
WRITELN(MOVES:8);
DOMOVE(30,18,BLANK) {INPUT NEXT MOVE}
END;
MOVE; {LETS YOU MOVE}
IF(PLAY) THEN ROBOTMOVE {MOVES THE ROBOTS}
END;
GOTOXY(0,21);
UnitClear(1); { Empty keyboard buffer }
WRITE('WOULD YOU LIKE TO PLAY AGAIN (Y OR N) ');
READ(M);
IF M IN ['N','n'] THEN AGAIN:=FALSE ELSE
BEGIN
PLAY:=TRUE;
GAMENU:=GAMENU+1;
IF WIN THEN
BEGIN
WINS:=WINS+1;
IF WINS>2 THEN DIFF:=IDIFF+1;
IF WINS>5 THEN DIFF:=IDIFF+2;
IF WINS>8 THEN DIFF:=IDIFF+3;
IF WINS>11 THEN DIFF:=IDIFF+4;
IF WINS>15 THEN DIFF:=IDIFF+6;
IF WINS>20 THEN DIFF:=IDIFF+8;
IF WINS>30 THEN DIFF:=IDIFF+12;
NROB:=DROB+2*DIFF;
IF NROB>ROBMAX THEN NROB:=ROBMAX
END;
END;
END;
END.
RobotChase is an open-source project, distributed under the terms of the GPL.
Copyright © 2007 Gem City Software. Distributed under the terms of the GPL