model module

The Model in the Model-View-Controller paradigm.

model.generateFoodGrid(width, height)[source]

This method returns a list of tuples containing all of the coordinates and colors for the view.actors.Food actors in the scene, given the specified width and height of the game board in conjunction with some of the constants defined in constants. No Food objects are to be instantiated directly in this method. Rather, this method is queried from elsewhere in the framework when it is the “appropriate” time to instantiate the Food objects. That is, given the width and height of the game board, this method computes

  1. The total number of Food objects that can fit in both the horizontal and vertical directions.
    • This is done using width, height, and the constants constants.FOOD_RADIUS and constants.FOOD_SPARSITY
    • You are advised to change the values of these constants to test your code, the default values do not accommodate for all corner cases. For example, what should your code do when you set constants.FOOD_SPARSITY to 1.0? If you set it to 2.0? What if you set constants.FOOD_RADIUS to be 23.32?
    • The total number of Food items that can fit in the \(x\) and \(y\) directions are nx and ny, respectively. That is, as a sanity check, the total length of the list you return at the end of the method should be exactly nx * ny, representing \(N = n_x \cdot n_y\) total Food objects based on the constraints of the problem.
  2. Computes the offset variables for the starting locations of each Food object, taking into account when constants.FOOD_RADIUS does not evenly divide width or height respectively.
    • The variables dx and dy in the code correspond to the change in position \(\Delta x\) and \(\Delta y\), respectively.
    • The variables tx and ty in the code correspond to the global translation \(t_x\) and \(t_y\), respectively.

Todo

  1. Using the variables from (1) and (2), compute all Food coordinate centers, and create a random color using view.display.randomColor(). At the stage that this method is called in the framework, this is all that can be computed.
    • Make sure you set constants.FULL_GAME_MODE to True , otherwise your hard work will never even be called by the framework!!!

Warning

As a reminder, you should not create any Food objects in this method!

Parameters:
width (float)

The total width of the game board at the start of the game (before any resizing by the user.)

height (float)

The total height of the game board at the start of the game (before any resizing by the user.)

Preconditions:
Size Constraints

width and height are both positive, and are both (individually) strictly greater than 2.0 * constants.FOOD_RADIUS * constants.FOOD_SPARSITY.

Return:
list

A length \(N = n_x \cdot n_y\) list of tuples representing each Food object. Ensure that the order is exactly correct. Each element of the list should be a tuple of length 3, where the following is true:

Index Type Value
0 float The center \(x\) coordinate \(c_x\).
1 float The center \(y\) coordinate \(c_y\).
2 PyQt4.QtGui.QColor A random color. You don’t need to construct one on your own, simply call the function view.display.randomColor() already imported for you at the top of the file.

Warning

The method calling this function (model.Scene.generate()) is going to loop through what you return similar to

lst = model.generateFoodGrid(w, h)
for cx, cy, color in lst:
    # ... create the actual Food object ...

So if the code crashes on a line similar to that one, you should inspect what you append to the list all_food and make sure it adheres to the above specification.

class model.Scene(controller, view)[source]

Bases: PyQt4.QtGui.QGraphicsScene

The main model of the game, responsible for creating and maintaining the state of the actors in the game. By inheriting from PyQt4.QtGui.QGraphicsScene, an instance of this class will be receiving the update notifications coming from the View indirectly. That is, though this class maintains the locations of the different Actors, because it is part of the PyQt4 View framework, it receives communication directly from the View in certain portions.

For example, the controller.CitizenPac class maintains a gameTimer that indirectly controls all of the Actors in the scene. At the end of the constructor for that class, the timer is connect ed to the model.Scene.advance() method in this class. In essence, this is an indirect “loop”, but since the timer is officially managed by the View, the impact is that the View technically communicates directly with the Model (this class). This relationship is instigated and controlled by the Controller, but we wanted to clarify by example that the relationships between the Model, View, and Controller in this framework have blurred boundary lines. If you decide to make your own Model-View-Controller one day, you may find yourself in a similar scenario – the limitations of the PyQt4 framework in the sense that technically the Model is “directly managed by the View, which is indirectly managed by the Controller” are confusing. However, though the relationships between these items become less translucent, its benefits (for technical reasons not explained here) are worthy. The short version: PyQt4 has a sophisticated backend framework developed over decades by one of the industry leaders. The framework excels at maintaining consistent internal state, and so we lean on these features at the expense of “blurred Model-View-Controller” relationships.

Attributes:
controller (controller.CitizenPac)

A reference to the Controller to be able to propagate events received from PyQt4 such as collisions back to the Controller so it can decide what action to perform.

view (PyQt4.QtGui.QGraphicsView)

A pointer to the QGraphicsView instance that this Scene is bound to. This reference is also accessible via the parent class’s parent member, stored as view for convenience. Every PyQt4.QtGui.QGraphicsScene instance must be bound to a PyQt4.QtGui.QGraphicsView instance — this is how the scene is actually displayed.

citizenPac (view.actors.CitizenPacActor)

The representation of CitizenPac.

ghosts (list)

A list of view.actors.GhostActor instances, representing all of the Ghosts in the scene.

food (list)

A list of view.actors.Food instances, representing where all of the Food in the scene is.

gameRunning (bool)

A boolean representing whether or not the game is currently running. In particular, collision events, moving actors, etc, should not continue if the game is not running. See the model.Scene.advance() method for how it is used.

foodEaten (int)

An integer representing how many Food collisions have been detected. When foodEaten == len(self.food), the controller is notified that the Game has completed.

generate(width, height)[source]

Responsible for creating the initial conditions of the game, including where CitizenPac and the Ghosts start, and the location of all the Food. This method calls the model.generateFoodGrid() method using the width and height of the PyQt4.QtGui.QGraphicsView instance that this (self) instance is bound to.

All instances created are registered using the model.Scene.registerActor() method, which is in turn responsible for storing the the generated actors in the related attributes of this instance (i.e. self.citizenPac or self.food).

If you want to extend the game to add more types of actors, this is where you should create them.

Parameters:
width (float)

The starting width of the window we will be drawing to.

height (float)

The starting height of the window we will be drawing to.

Warning

Do not call this method from the constructor. The controller.CitizenPac class is responsible for calling this method. Because of the “blurred Model-View-Controller” relationships, the Scene must be instantiated before the View has completed initialization. In order to use the correct width and height, initialization of the actors in the scene must be deferred until after the View’s Layout has been performed (this is controlled by PyQt4).

registerActor(actor, cx, cy)[source]

Registers an view.actors.Actor with this Scene. This method must be called for the game mechanics (e.g. collisions) to be detected. It is assumed that the actor being registered has already been added to the PyQt4 side of the scene by construction. That is, the model.Scene.generate() function will instantiate an actor passing self as the first parameter. Since each actor extends the PyQt4.QtGui.QGraphicsItem class, the item is automatically added to the Scene. Alternatively, you can call the addItem method (inherited from the PyQt4.QtGui.QGraphicsScene class) to associated the item with the graphics backend.

After construction, the PyQt4.QGraphicsItem class has not had its position set. The setPos function is called at the end of this method, assuming it was an instance of a class we are currently tracking.

Parameters:
actor (view.display.Actor)

An view.display.Actor instance. Currently the only classes that are registered are

  1. view.display.CitizenPacActor (stored in self.citizenPac).
  2. view.display.GhostActor (appended to self.ghosts).
  3. view.display.Food (appended to self.food).

If you want to extend the game to allow more actors, declare the appropriate variables in the constructor of this class to maintain their state, and register them here.

cx (float)

The \(x\) coordinate of the actor.

cy (float)

The \(y\) coordinate of the actor.

Preconditions:
Size Constraints

The parameters cx and cy are assumed to be valid coordinates for this scene. If they are not, the actor will simply be “displayed” off-screen.

numFoodEaten()[source]

Returns the number of food items that have been eaten in this round of the game.

Return:
int

The number of food eaten so far in this round of the game.

setRunning(running)[source]

Pass-through method for the Controller to toggle whether or not the game is currently-running, used to avoid processing collisions before the game is resumed. Simply sets self.gameRunning to the value of running.

Parameters:
running (bool)

Whether or not the game is currently running.

reset()[source]

This method resets the game state for all actors in the scene, and ensures that all food becomes edible again. This method should not modify any instances directly — call the view.actors.Actor.reset() for the appropriate entities of this instance.

Tip

You are encouraged to refer to the implementation of model.Scene.advance(), in addition to the parent class documentation of view.actors.Actor, to complete making food edible.

wrapActor(actor, width, height)[source]

This method is responsible for adjusting the position of an Actor so that it remains within the confines of the game grid.

Tip

Refer to the 3.2   Coordinate system and actor coordinates section of the writeup

Parameters:
actor (view.actors.Actor)

The actor to modify the position of, based off width and height.

width (float)

The width of the bounding rectangle that defines the coordinate system for this scene.

height (float)

The height of the bounding rectangle that defines the coordinate system for this scene.

Preconditions:
Size Constraints

The width and height must be greater than or equal to 1.0.

wrapRelevantActors()[source]

Confines all relevant actors to remain within the bounds of the game grid by calling model.Scene.wrapActor() for every actor that can move in the game. If you desired to have food moving, you would need to update this method to do this as well!

advance()[source]

This method is what the controller.CitizenPac hooks its gameTimer to. It is responsible for processing three things:

  1. Process collisions when self.gameRunning and constants.FULL_GAME_MODE are both True.
  2. Confine actors to the game grid via model.Scene.wrapRelevantActors().
  3. Call the super class advance, which will propagate to the Actors.

The third point there is particularly important not to omit, otherwise the animations on the food, decisions about moving, etc will not occur.

keyPressEvent(e)[source]

If the key pressed is one of w, s, d, or a, call the view.actors.CitizenPacActor.queueMove() actor with the appropriate move direction and True to signal that the user requested CitizenPac to move in this direction.

If it is not one of these keys, call the super class keyPressEvent to allow other keys to be applied elsewhere in the framework.

Parameters:
e (PyQt4.QtGui.QKeyEvent)

The key event provided by the Qt backend.

keyReleaseEvent(e)[source]

If the key released is one of w, s, d, or a, call the view.actors.CitizenPacActor.queueMove() actor with the appropriate move direction and False to signal that the user has stopped requesting that CitizenPac move in this direction.

If the space key is released, then the user has requested to pause or resume the game, call the controller.CitizenPac.gameRunningSwitched() function.

If the r key is released, then the controller.CitizenPac.replay() method is called. Note this method does not reset the game unless it has been won or lost, so the r key only has an effect at the end of the game.

If it is not one of these keys, call the super class keyPressEvent to allow other keys to be applied elsewhere in the framework.

Parameters:
e (PyQt4.QtGui.QKeyEvent)

The key event provided by the Qt backend.