Queen's Logo

Beyond Code: An Introduction to Model-Driven Software Development (CISC 844, Winter 2025)

Assignment 3 (MDSD with HCL Model RealTime)

Due: Sun, March 16

Intro

  1. Purpose of the Assignment:
    1. Give you practical experience with timed systems, replication, and unwired ports (in the context of UML-RT and RTist) (Part I).
    2. Show how the model can be integrated with a GUI (Part I).
    3. Illustrate how UML-RT's support for dynamic instance and connector creation can be used to increase the generality of an application (Part II).
  2. Preparation:
    1. The RTist installation that you used for Assignment 1 will also be used for this assignment.
    2. Make sure that you are aware of the design guidelines discussed in class.

Part I (35 points total)

  1. Problem Description:
    Space Invaders is considered one of the most successful and influential video games of all time. The goal of the game is to destroy alien spaceships that slowly descend from the top of the screen. The player operates a spaceship at the bottom that can move sideways and shoot. The player wins once all alien spaceships are destroyed. The player loses if they have been hit a certain number of times, or the aliens reach the bottom of the screen.

    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.

  2. Description of provided UML-RT model:
    Most capsules in the provided model directly correspond to objects in the game (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.

    Capsule diagram of Space Invader model

    • Capsule Top: The purpose of Top is to
      1. start the game by sending the message start() to player, enemies and bunkers (via internal port startGameP),
      2. interact with the user, i.e.,
        • it periodically uses the SDL library to check for keyboard input. In case of movement commands, 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.
        • it listens to 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).
      3. end the game by listening on port 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.
      We note the separation of concerns here: The use of SDL library is restricted to 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.
    • Rendered capsules (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).
      Some of the rendered capsules have connectors between them. E.g., via the 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.
    • Capsule 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.
    • Capsule 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.
    • Capsule 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.
  3. How to run the generated code:
    The following instructions assume Windows. They should be similar for other OSs.
    • Open a 'Cygwin64 Terminal' (type 'Cygwin64 Terminal' into the search bar) and navigate to the Eclipse workspace subfolder containing the execuable:
      >> cd /cygdrive/c/Users/dingel/eclipseWorkspacesAndRuntimes/rtist112e_spaceInvaders/SpaceInvadersV0_target/default
    • Download the font file MontereyFLF.ttf into the directory containing the executable.
    • Start the X server and set the DISPLAY environment variable:
      >> startxwin &
      >> export DISPLAY=:0.0
      To check that DISPLAY has been set correctly use echo $DISPLAY
    • Invoke the executable as usual:
      >> ./executable -URTS_DEBUG=quit
  4. Problems in the provided model:
    While the structure of the model is complete (i.e., it contains all necessary capsules, ports, protocols, and connectors), its behaviour is not: players and enemies cannot shoot and enemies don't move. Your task is to modify the state machines of some capsules appropriately to correct this. If you complete all parts of this assignment correctly, game play should roughly look like in this Youtube video (MP4 for higher resolution).
  5. Task description:
    • Task 0: Prep [0 points] Download the model SpaceInvadersV0.zip, import it, inspect it, and generate code from it. Build and run the generated code.
    • Task 1: Allow player to shoot [5 points] Extend 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.
    • Task 2: Allow enemies to move Implement this task using a sequence of three subtasks. For all subtasks, each enemy still alive should update its location using the standard update frequency (0.01 seconds). Use operation 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.
      • Subtask a: [5 points] From their initial position, enemies should move to the right until an enemy reaches the right screen border and then stop.
      • Subtask b: [5 points] From their initial position, enemies should move right and left forever. I.e., while moving into a direction, once an enemy has reached a border, all enemies should start moving into the opposite direction.
      • Subtask c: [10 points] From their initial position, enemies should move right and left in alternating fashion as described for Subtask b. However, once a border is reached, they should all go down one row, before they start moving into the opposite direction. This process should continue until an enemy reaches the bottom. When an enemy reaches the bottom, the player loses and game should end.
      • Subtask d: [5 points] In a text file called ReadMe.txt (which, as for the previous two assignments, is part of your project) briefly describe how you have implemented Task 2.
    • Task 3: Allow enemies to shoot [5 points] Whenever the position of an enemy is updated, the enemy should have a 0.02 (2%) probability to shoot. To shoot, it sends a shoot(bLoc) message to the enemy bullets where bLoc is the initial position of the bullet (use operation createInitialBulletLocation() to compute this).
  6. Acknowledgement:
    This assignment is based on the CISC 844 project of Fangjian Lei and Yiping Jia from Winter 2023.

Part II (5 points total)

  1. Problem Description:
    We now briefly return to the problem of giving change discussed earlier. We observe the following about all three designs in Part II of Assignment 2:
    • Coins are dispensed in order of decreasing denominations (i.e., first all toonies, then all loonies).
    • The amount to give loonies for is given by what is left over once the maximal number of toonies has been dispensed.
    • Ignoring the denominations, the process of giving change is the same for toonies and for loonies (check that the amount to give change for is greater or equal than the coin denomination d; if yes, dispense a coin, subtract d from the amount to give change for, and repeat; if not, terminate).
    These observations motivate the next design of the changer capsule:

    Capsule diagram of Verion 4 of Changer capsule

    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:

    • a stage receives the amount that (still) needs to be given change for from the previous stage (via port 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),
    • the first and last stage connect the pipeline to the Changer capsule such that
      • the first stage receives the initial amount to give change for from Changer, and
      • the last stage sends remaining amount to Changer, and
    • each stage is an instance of the same capsule (called 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

    • the Stage capsule needs to be instantiated four times,
    • these four instances need to be connected to form a pipeline inside the changer instance,
    • the stage instances need to be customized so that they give 200-cent coins, 100-cent coins, 10-cent coins, and 1-cent coins respectively.

    The last design (GiveChangeV5) implements this using the following UML-RT features:
    • optional capsule instances (which can be created and destroyed at runtime), and
    • unwired ports that are wired at runtime by the application using service access (SAPs) and provision points (SPPs).
    Also, instead of sending the denomination to each stage via an initialization message, this information is provided by the changer to the instance as an argument to the 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).

    Capsule diagram of Verion 5 of Changer capsule

  2. Task description:
    Import and build the model realizing this fully general process of giving change (GiveChangeV5_forA3.zip). Consider the system execution caused by the following invocation:
          $ ./executable.exe -URTS_DEBUG=quit -UARGS 1575 2000 200 100 10 5    
    Execute 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.

What to hand in:

Marking:

For Tasks 1 to 3 of Part I, your changes to the model will be marked based on their correctness and completeness (with respect to the assignment instructions and the system description), but also using the design guidelines discussed in class. Your answer to Subtask 2.d will be marked on clarity and correctness.


Last modified: Sun Jan 05 2025 12:39:59