![]() |
Beyond Code: An Introduction to Model-Driven Software Development (CISC 844, Winter 2025)
Assignment 3 (MDSD with HCL Model RealTime)Due: Sun, March 16
|
The provided model implements the game in UML-RT using the SDL library for keyboard input and graphics. It is, however, incomplete. Your task is to complete the model as described below.
Player
, Enemy
, Bunker
, PlayerBullet
, and
EnemyBullet
). Since instances of these capsules also appear on the screen, we will
call these rendered capsules and their instances rendered objects.
Also, we will use the terminology
player, enemy, bunker, player bullet, and enemy bullet
to refer to instances (objects) of the corresponding classes.
Apart from the rendered capsules, there are three helper capsules:
EnemyHarness
, Enemy2BulletConn
, and
Bullet2BunkerConn
.
The Top
capsule contains all of these capsules and is connected to all rendered capsules
and the harness via internal ports.
Top
:
The purpose of Top
is to
start()
to player,
enemies and bunkers (via internal port startGameP
),
Top
sends a move(direction:int)
message to the player via internal port inputP
instructing it to move
left or right. In case of a quit command (i.e., down-arrow key), it terminates the game.
render(shape:Shape)
messages from the rendered
objects which will cause it to use the SDL library to render
the argument shape. On screen, rendered objects are represented by rectangles
of different dimensions (i.e., width, height) and colour (i.e., RGB values).
The Shape
data type encapsulates this information together with x
and y coordinates.
Screen objects tell Top
about how they should be rendered using
SDL by sending it a render(shape:Shape)
message.
To make a shape disappear on the screen, a new shape is rendered with the
same dimensions and at the same location but in black colour (i.e., RGB values 0,0,0).
endGameP
for messages that
indicate that a game ending state has been reached (playerLoses()
and playerWins()
).
Message playerLoses()
is sent from an enemy that has reached the bottom of the screen
or from the player if it has been hit by an enemy bullet with no lives left.
playerWins()
is sent by the harness when all enemies have been killed.
In response, Top
ends the game after using the SDL library
to display a corresponding message on the screen.
Top
, while Top
holds no data related to the game or
the position and status of objects and how they are displayed on the screen.
All this information is kept by the rendered objects themselves.
Player
, Enemy
, PlayerBullet
,
EnemyBullet
, Bunker
):
Capsules of objects appearing on the screen have attributes that record the width, height,
RGB values and position of their shape.
Capsules of moving objects (i.e., all rendered capsules except Bunker
) also contain
information about their speed (i.e., number of pixels) with which they can move per screen update,
as well as the frequency of these updates (typically every 10^7 nanoseconds, i.e., 0.01 seconds).
Capsules of objects that can 'die' or disappear (Player
, Enemy
,
Bunker
) have attributes allowing them to keep track of their status and
determine when to die (e.g., healthPoints:int
for Bunker
and
livesLeft:int
for Player
).
shootP
port a player can send a message
shoot(loc:Location)
to the player bullet saying that a
bullet should be fired from the argument location (datatype
Location
encapsulates x:int
and y:int
coordinates).
Also, both types of bullet are connected with the objects they can hit.
E.g., every time a flying player bullet updates its location, it will send
a check(loc:Location)
message to all bunker and enemy objects. Upon the receipt
of this message, the receiving object will check if it has been hit
by that bullet. If so, it will respond with a hit()
or a hitBy(id:int)
(in case of an enemy bullet hitting a bunker) which in turn will
cause the bullet to stop flying, disappear from the screen and
become available again.
Enemy2BulletConn
:
This capsule helps implement the many-to-many communication between
enemies and enemy bullets.
To shoot, an enemy can send a shoot(loc:Location)
message
via shootP
. Enemy2BulletConn
then passes this message to one of the three enemy bullets.
Bullet2BunkerConn
:
The second many-to-many communication takes place between enemy bullets and bunkers and
this capsule helps implement it. The check(loc:Location)
message
from an enemy bullet is broadcast by Bullet2BunkerConn
to all bunkers.
In case of a hit, the affected bunker will respond with hitBy(id:int)
where id
identifies the bullet. Bullet2BunkerConn
uses that
id to pass on the hit information to the correct bullet.
EnemyHarness
:
The wired connector (port harnessP
) between the harness and the enemies allows the
harness to determine when all enemies are ready to start the game (message
ready()
)
or all enemies have been killed (message killed()
).
The harness and the enemies also have unwired ports.
When the game starts (message start()
from Top
), the enemies use
these ports to connect themselves in a
circular fashion with the harness serving as the
start and end of the circle.
The receipt of ready()
messages
from all enemies indicates to the harness that this dynamic wiring
has completed and that the enemies are ready for action.
>> cd /cygdrive/c/Users/dingel/eclipseWorkspacesAndRuntimes/rtist112e_spaceInvaders/SpaceInvadersV0_target/default
MontereyFLF.ttf
into the directory containing the executable.
DISPLAY
environment variable:
>> startxwin &
>> export DISPLAY=:0.0
DISPLAY
has been set correctly use echo $DISPLAY
>> ./executable -URTS_DEBUG=quit
Top
so that the user can input shoot commands via the upward-arrow on the
keyboard (see this Youtube video).
In this case, Top
should send a shoot()
message to the player.
Upon receipt of this message, the player should send a shoot(bLoc)
message to
the player bullet where bLoc
is the initial location of the bullet. Use operation
createInitialBulletLocation()
to compute this location.
updateLocation()
to compute the new location and
boolean operations atRightBorder()
, atLeftBorder()
, and atBottom()
to determine if an enemy has reached a border or the bottom, as appropriate.
ReadMe.txt
(which, as for the previous two assignments,
is part of your project) briefly describe how you have implemented Task 2.
shoot(bLoc)
message to the enemy bullets where bLoc
is the initial position of the bullet (use operation createInitialBulletLocation()
to compute this).
In this design, each of the two coin denominations has its own capsule instance
(called toonies
and loonies
).
These instances are connected to form the stages of a pipeline
in which:
prevP
);
then, it gives as much change as it can (using port coinsP
), and
passes on the remaining the amount to the next stage (via nextP
),
Changer
capsule such
that
Changer
, and
Changer
, and
Stage
),
and thus executes the same state machine. The changer capsule lets
each stage know which denomination it is responsible for
via an initialization message (sent via port initPipelineP
).
The Changer
capsule also
checks if the amount returned from the pipeline at
the end of the change process is equal to 0.
This design now shows us how to come up with a fully
general solution, i.e., a version of the changer that is
parametric in the number of coins that are used for giving
change and their respective denominations. For example, we want
the invocation
$ ./executable.exe -URTS_DEBUG=quit -UARGS 81 400 200 100 10 1
to dispense one 200-cent coin, one 100-cent coin, one 10-cent coin, and nine 1-cent coins.
That is, on the command line, the value of the selected item and the amount of money
inserted is followed by a monotonically decreasing list of integers
indicating the denominations of the coins to use (the number of
coins is given by the length of this list):
$ ./executable.exe -URTS_DEBUG=quit -UARGS <ins> <sel> <denomination of highest value coin> ... <denomination of lowest value coin>
For this to be possible, the pipeline needs to be built dynamically
(i.e., at runtime) according to the command line arguments provided.
E.g., in the invocation above
Stage
capsule needs to be instantiated four times,
GiveChangeV5
) implements this using the following
UML-RT features:
incarnate
function. Note how the capsule diagram of the Changer
capsule
does not show the pipeline (because its shape (length) is unknown
prior to runtime), but does
show that the stage instances are optional and that their pipeline ports
prevP
and nextP
are unwired at the start (and, thus,
wired dynamically).
$ ./executable.exe -URTS_DEBUG=quit -UARGS 1575 2000 200 100 10 5Execute and inspect the model to understand the resulting execution. Draw a sequence diagram showing all the messages exchanged between all capsule instances (i.e.,
Top
, harness
, changer
,
and all pipeline stages) as a result of this invocation. Your diagram
should also include calls to the incarnate
function, if any.
If messages or calls to incarnate
carry data, please
include them as well.
Put your sequence diagram into your Space Invader project from Part I and give
it the name Part2
.
[firstName]_[lastName]_A3_CISC844_W25.zip
as name
where [firstName]
and [lastName]
are replaced by your first and last names, respectively.
Upload this archive to OnQ.