联系方式

  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-23:00
  • 微信:codinghelp

您当前位置:首页 >> C/C++编程C/C++编程

日期:2022-05-22 01:59

Maze Runner Once Again

Assignment 3

Semester 1, 2022

CSSE1001/CSSE7030

Due date: 3rd June 2022 18:00 GMT+10

1 Introduction

In assignment 2 you implemented a game of MazeRunner with a text-based interface. The Apple

MVC design pattern used in this game allows modelling logic and view classes to be modified

fairly independently. In this assignment, you’ll exchange the text-based interface of MazeRunner

with a more sophisticated tkinter-based Graphical User Interface (GUI).

The game view initially contains 3 components:

? A level view on which the tiles and entities are drawn using either coloured shapes or images;

? An inventory view, which appears to the right of the level view and allows users to apply

collected items (other than coins) by left-clicking those items in their inventory; and

? A stats view, which shows the player’s stats, as well as the number coins they have collected.

As opposed to assignment 2, where users controlled the game by inputting text at a prompt, in

assignment 3 users control the game through key-presses and mouse clicks. An example of the

final game is shown in Figure 1.

You can find the tkinter documentation on effbot1 and New Mexico Tech2

.

2 Tips and hints

This assignment is split into two tasks for undergraduate students, and an additional third task

for postgraduate task. The number of marks associated with each task is not an indication of

difficulty. Task 1 may take less effort than task 2, yet is worth significantly more marks. A fully

functional attempt at task 1 will likely earn more marks than attempts at both task 1 and task

2 that have many errors throughout. Likewise, a fully functional attempt at a single part of task

1 will likely earn more marks than an attempt at all of task 1 that has many errors throughout.

You should be testing regularly throughout the coding process. Test your GUI manually

and regularly upload to Gradescope to ensure the components you have implemented pass the

Gradescope tests. At the minimum you should not move on to task 2 until you pass the task 1 tests.

Except where specified, minor differences in the look (e.g. colours, fonts, etc.) of the GUI are

acceptable. Except where specified, you are only required to do enough error handling such that

1https://web.archive.org/web/20171112065310/http://effbot.org/tkinterbook

2https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/index.html

1

Figure 1: Example fully functional game at the end of Task 2.

regular game play does not cause your program to crash or error. If an attempt at a feature

causes your program to crash or behave in a way that testing other functionality becomes difficult

without your marker modifying your code, comment it out before submitting your assignment. If

your solution contains code that prevents it from being run, you will receive a mark of 0.

You must only make use of libraries listed in Appendix A. You must not import anything that

is not on this list; doing so will result in a deduction of up to 100% of your mark.

You may use any course provided code in your assignment. This includes any code from the

support files or sample solutions for previous assignments from this semester only, as well as

any lecture or tutorial code provided to you by course staff. However, it is your responsibility to

ensure that this code is styled appropriately, and is an appropriate and correct approach to the

problem you are addressing.

2

3 Task 1: Basic Gameplay - 10 marks

Task 1 requires you to implement a functional GUI-based version of MazeRunner. At the end of

this task your game should look like Figure 2. A heading label should appear at the top of the

window at all times. Below this, the three components should appear as described in Section 1

and as depicted below.

Figure 2: Game at end of task 1.

Certain events should cause behaviour as per Table 1.

Event Behaviour

Key Press: ‘w’ Player attempts to move up one square.

Key Press: ‘a’ Player attempts to move left one square.

Key Press: ‘s’ Player attempts to move down one square.

Key Press: ‘d’ Player attempts to move right one square.

Left click on an

item in inventory

One item of the kind clicked should be applied to the player and removed from

the inventory. If no instances of the item remain in the inventory, the label

showing the item should be removed from view, and all other item labels below

it should move up to fill the space.

Table 1: Events and their corresponding behaviours.

3

In task 1, tiles are represented by coloured rectangles and entities are represented by coloured

circles. You must also annotate the circles of entities with their id (as per Fig. 2). The colours

representing each tile and entity can be found in constants.py. You must use the colours speci-

fied in constants.py.

When the player wins or loses the game they should be informed of the outcome via a messagebox.

The messagebox must display the text of WIN MESSAGE or LOSS MESSAGE in constants.py. For

task 1, there is no required behaviour after the messagebox is closed; the program can terminate

or remain open. However, whatever behaviour you choose must be reasonable (i.e. closing the

messagebox should not cause your program to crash, or an error to show).

To complete this task you will need to implement various view classes, as well as a graphical

controller class which extends the existing MazeRunner controller class. While it is not necessary

in order to complete task 1, you are permitted to add additional modelling classes if they improve

your solution.

The following sub-sections outline the required structure for your code. You will benefit from

writing these classes in parallel, but you should still test individual methods as you write them.

3.1 Provided Code

The following code is provided in a2 solution.py and a3 support.py for you to use when implementing

your solution.

3.1.1 Model classes

Model classes should be defined as per Assignment 2. You may add more modelling classes if they

improve the code. You may not modify the supplied model classes.

3.1.2 AbstractGrid

AbstractGrid is an abstract view class which inherits from tk.Canvas and provides base functionality

for multiple view classes. An AbstractGrid can be thought of as a grid with a set number

of rows and columns, which supports creation of text and shapes at specific (row, column) position.

Note that the number of rows may differ from the number of columns, and may change

after the construction of the AbstractGrid. If the dimensions change, the size of the cells will

be be updated to allow the AbstractGrid to retain its original size. AbstractGrid consists of the

following interface:

? init (self, master: Union[tk.Tk, tk.Frame], dimensions: tuple[int, int],

size: tuple[int, int], **kwargs) -> None:

Sets up a new AbstractGrid in the master frame. dimensions is the initial number of rows

and number of columns, and size is the width in pixels, and the height in pixels. **kwargs

is used to allow AbstractGrid to support any named arguments supported by tk.Canvas.

? set dimensions(self, dimensions: tuple[int, int]) -> None:

Sets the dimensions of the grid to dimensions.

? get bbox(self, position: tuple[int, int]) -> tuple[int, int, int, int]:

Returns the bounding box for the (row, col) position, in the form (x min, y min, x max,

y max).

? get midpoint(self, position: tuple[int, int]) -> tuple[int, int]:

Gets the graphics coordinates for the center of the cell at the given (row, col) position.

4

? annotate position(self, position: tuple[int, int], text: str) -> None:

Annotates the cell at the given (row, col) position with the provided text.

? clear(self) -> None: Clears the canvas.

3.2 View classes

You must implement the view classes for the level map, inventory, and player stats. Because

the level map and player stats can both be represented by grids, you are required to use the

AbstractGrid class to factor out the shared functionality. This section outlines the classes and

methods you are required to write. While you do not need to, you are permitted to add additional

methods or classes provided they improve your solution.

3.2.1 LevelView

LevelView is a view class which inherits from AbstractGrid and displays the maze (tiles) along

with the entities. Tiles are drawn on the map using coloured rectangles at their (row, column)

postitions, and entities are drawn over the tiles using coloured, annotated ovals at their (row,

column) positions (as per Fig. 2). The colours representing each tile and entity can be found in

constants.py. You must use these colours.

Your program should work for reasonable map sizes. You must not assume that the number

of rows will always be equal to the number of columns. You must use the create oval,

create rectangle, and create text methods for tk.Canvas to achieve this task. The LevelView

class should be instantiated as LevelView(master, dimensions, size, **kwargs) where

**kwargs should be passed to the super class as with AbstractGrid. The width and height of the

maze shown in the level (and consequently, the level view) are given in constants.py. While it is

recommended to create your own helper methods in this class, the only method you are required

to create is:

? draw(self, tiles: list[list[Tile]], items: dict[tuple[int, int], Item],

player pos: tuple[int, int]) -> None:

Clears and redraws the entire level (maze and entities).

3.2.2 StatsView

StatsView is a view class which inherits from AbstractGrid and displays the player’s stats (HP,

health, thirst), along with the number of coins collected (as per Fig. 2). A StatsView always has

four columns and two rows. The first row contains the headings for the types of stats and the

second row contains the values for the stat in that column. You are required to implement the

following methods in this class:

? init (self, master: Union[tk.Tk, tk.Frame], width: int, **kwargs) -> None:

Sets up a new StatsView in the master frame with the given width. The height of

the StatsView can be found in constants.py. The background colour should be set to

THEME COLOUR from constants.py via the **kwargs.

? draw stats(self, player stats: tuple[int, int, int]) -> None:

Draws the player’s stats (hp, hunger, thirst).

? draw coins(self, num coins: int) -> None: Draws the number of coins.

Again, it may be beneficial to create your own helper methods in this class.

5

3.2.3 InventoryView

InventoryView is a view class which inherits from tk.Frame, and displays the items the player

has in their inventory via tk.Labels, under a title label. This class also provides a mechanism

through which the user can apply items. You must implement the following methods in this class:

? init (self, master: Union[tk.Tk, tk.Frame], **kwargs) -> None:

Creates a new InventoryView within master.

? set click callback(self, callback: Callable[[str], None]) -> None:

Sets the function to be called when an item is clicked. The provided callback function

should take one argument; the string name of the item.

? clear(self) -> None: Clears all child widgets from this InventoryView.

? draw item(self, name: str, num: int, colour: str) -> None:

Creates and binds (if a callback exists) a single tk.Label in the InventoryView frame. name

is the name of the item, num is the quantity currently in the users inventory, and colour is

the background colour for this item label (determined by the type of item).

? draw inventory(self, inventory: Inventory) -> None:

Draws any non-coin inventory items with their quantities and binds the callback for each, if

a click callback has been set. Hint: loop over each item in the inventory and call draw item

for each to create and bind the item label.

3.2.4 GraphicalInterface

GraphicalInterface inherits from UserInterface and must implement the methods described

by UserInterface. The GraphicalInterface manages the overall view (i.e. the title banner

and the three major widgets), and enables event handling. You must implement the following

methods:

? init (self, master: tk.Tk) -> None: Creates a new GraphicalInterface with master

frame master. Sets the title of the window to ‘MazeRunner’ and creates a title label.

Note: this method is not responsible for instantiating the three major components, as you

may not know the dimensions of the maze when the GraphicalInterface is instantiated.

? create interface(self, dimensions: tuple[int, int]) -> None:

Creates the components (level view, inventory view, and stats view) in the master frame

for this interface. dimensions represent the (row, column) dimensions of the maze in the

current level.

? clear all(self) -> None: Clears each of the three major components (do not delete the

component instances, just clear them).

? set maze dimensions(self, dimensions: tuple[int, int]) -> None:

Updates the dimensions of the maze in the level to dimensions.

? bind keypress(self, command: Callable[[tk.Event], None]) -> None:

Binds the given command to the general keypress event. The command should be a function

which takes in the keypress event, and performs different actions depending on what character

was pressed. Any character other than ‘w’, ‘a’, ‘s’, or ‘d’ should be ignored (including

all uppercase letters).

6

? set inventory callback(self, callback: Callable[[str], None]) -> None:

Sets the function to be called when an item is clicked in the inventory view to be callback.

The callback function will need to be constructed in the controller class (see Section 3.3),

and must take one argument (the string name of an Item), and apply the first instance of

that item in the inventory to the player. This function should then remove that item from

the player’s inventory.

? draw inventory(self, inventory: Inventory) -> None:

Draws any non-coin inventory items with their quantities and binds the callback for each,

if a click callback has been set.

? draw(self, maze: Maze, items: dict[tuple[int, int], Item], player position:

tuple[int, int], inventory: Inventory, player stats: tuple[int, int, int])

-> None: Must implement the draw method as per the docstring in the UserInterface

class. This should just involve clearing the three major components and redrawing them

with the new state (provided as arguments to this method).

? draw inventory(self, inventory: Inventory) -> None:

Must implement the draw inventory method as per the docstring in the UserInterface

class. Note: this method will need to draw both the non-coin items on the inventory view

(see public draw inventory method), and also draw the coins on the stats view.

? draw level(self, maze: Maze, items: dict[tuple[int, int], Item],

player position: tuple[int, int]) -> None:

Must implement the draw level method as per the docstring in the UserInterface class.

? draw player stats(self, player stats: tuple[int, int, int]) -> None:

Must implement the draw player stats method as per the docstring in the UserInterface

class.

3.3 Controller class

3.3.1 GraphicalMazeRunner

GraphicalMazeRunner should inherit from MazeRunner and overwrite / add methods where required

in order to enable the game to use a GraphicalInterface instead of a TextInterface,

and to be based on events generated by the user (keypresses and mouse clicks), rather than based

on user input to the shell. In particular, you will need to implement the following methods:

? init (self, game file: str, root: tk.Tk) -> None: Creates a new GraphicalMazeRunner

game, with the view inside the given root widget.

? handle keypress(self, e: tk.Event) -> None: Handles a keypress. If the key pressed

was one of ‘w’, ‘a’, ‘s’, or ‘d’ a move is attempted. If the player wins or loses the game with

this move, they are informed of their result via a messagebox.

? apply item(self, item name: str) -> None: Attempts to apply an item with the

given name to the player.

? play(self) -> None: Called to cause gameplay to occur. This method should first create

the widgets on the GraphicalInterface, bind the keypress handler, set the inventory

callback, and then redraw to allow the game to begin.

7

3.4 play game(root: tk.Tk) function

The play game function should be fairly short. You should:

1. Construct the controller instance using the file in the GAME FILE constant and the root tk.Tk

parameter.

2. Cause gameplay to commence.

3. Ensure the root window stays opening listening for events (using mainloop).

3.5 main function

The main function should:

1. Construct the root tk.Tk instance.

2. Call the play game function passing in the newly created root tk.Tk instance.

4 Task 2: Images, Buttons, and File Menu

Task 2 requires you to add additional features to enhance the games look and functionality. Figure

1 gives an example of the game at the end of task 2. Another example is shown below in

Figure 3. Note: Your task 1 functionality must still be testable. When your program

is run with the TASK constant in constants.py set to 1, the game should display only

task 1 features. When your program is run with the TASK constant set to 2, the game

should display all attempted task 2 features. There should be no task 2 features visible

when running the game in task 1 mode.

As an advanced task, the structure of your code is largely up to you. However, if you complete

this task, you will be marked on how well-designed your code is. Some aspects of design are

compulsory (e.g. implementing the subclasses described in subsections below). However, the

methods and internal design of these classes are not prescribed. When designing your solution,

you should consider ways to effectively utilize the inheritance structure to avoid duplicating code.

4.1 Images

Create a new view class, ImageLevelView, that extends your existing LevelView class. This class

should behave similarly to the existing LevelView class, except that images should be used to display

the tiles and entities, rather than rectangles and ovals (see the provided images folder). When

your assignment is run with the TASK constant in constants.py set to 2, the ImageLevelView

should be displayed. When it is run with the TASK constant in constants.py set to 1, the basic

LevelView from task 1 should be displayed.

Just as the rectangles and ovals needed to change size depending on the dimensions of the level

maze in task 1, in task 2, the images must resize appropriately. In order to do this, you will need

to install Pillow3

, and then import ImageTk and Image from PIL.

3https://pillow.readthedocs.io/en/stable/installation.html

8

Figure 3: Another example fully functional game at the end of Task 2.

4.2 ControlsFrame

Add a new class ControlsFrame which inherits from tk.Frame, and displays two buttons (restart

and new game), as well as a timer of how long the current game has been going for. The

ControlsFrame instance should be displayed at the bottom of the interface, just below the

StatsView. The details of the three components on this widget are as follows:

? Restart button: when this button is clicked, the current game should be restarted, i.e. the

user should return to the start of the first level, their move count and stats should reset,

and the game timer should return to 0.

? New game button: when this button is clicked, a tk.TopLevel window should appear,

prompting the user to enter a relative path to a new game file. If the user enters a valid

game file, the game should restart using this game file. If they enter an invalid game file,

they should be informed that the game file was not valid via a messagebox. Once the user

has acknowledged the messagebox, the toplevel window should close and the user should

not be automatically reprompted.

? The game timer should display the number of minutes and seconds that have elapsed since

the current game began.

4.3 File Menu

Add a file menu with the options described in Table 2. Note that on Windows this will appear in

the window, whereas on Mac this will appear at the top of your screen. On Mac, the file menu

should look something like Figure 4. The exact layout is not overly important (e.g. your menu

9

may have a dashed line at the top, or other minor differences in display), as long as the options

are present with the correct text, and correct functionality.

Figure 4: Example file menu on Mac.

For saving and loading files, you must design an appropriate file format to store information

about games. You may use any format you like, as long as you do not import anything that

has not been explicitly permitted in Appendix A, and your save and load functionality work

together. Reasonable actions by the user (e.g. trying to load a non-game file) should be handled

appropriately (e.g. with a pop-up to inform the user that something went wrong).

Option Behaviour

Save game Prompt the user for the location to save their file (using an appropriate

method of your choosing) and save all necessary information

to replicate the current state of the game.

Load game Prompt the user for the location of the file to load a game from and

load the game described in that file.

Restart game Restart the current game, including game timer.

Quit Prompt the player via messagebox to ask whether they are sure

they would like to quit. If no, do nothing. If yes, quit the game

(window should close and program should terminate).

Table 2: File menu options.

5 Postgraduate Task: Shop and shop-exclusive items

Note: functionality for this task should only be present when the TASK constant is set to 3. None

of the postgraduate features should be present in task 1 or 2 mode.

For this task, you’ll create a shop where the user can spend their collected coins to buy items, and

integrate this shop into your assignment via a new Button on the ControlsFrame. Note that this

button should not be present in your task 2 functionality. Additionally, you’ll extend the model

to add a item, that will be exclusively available in the shop, candy.

5.1 Basic Shop

Create a basic shop interface with the four items along with their prices:

Apple $1

Water $1

Honey $2

Potion $2

10

(a) Basic shop front before purchasing anything. (b) Basic shop front after purchasing two items.

Figure 5: Basic shop.

The store front should also have a ‘Done’ button, which closes the shop when clicked. The interface

should look as shown in Figure 5a. Note: you cannot create more than one tk.Tk instance.

Instead, you should set up the store front as a tk.Toplevel.

When the image of an item is left clicked, if the player can afford the item (has enough coins),

then the price should be subtracted from the player’s coins, and an instance of the item should

be added to the player’s inventory. An example of this is shown in Figure 5b, which shows the

interface from Figure 5a immediately after left clicking water and honey once each.

5.2 Candy

For this task, you will extend the existing model to add a new item that the player can buy from

the store. At the end of this task, your store front should look like Figure 6.

You must implement a new Food subclass called Candy. A candy should restore the player’s

hunger to 0 when applied, but also reduce their health by 2. Add the Candy item to the store

front, with a price of $3.

11

Figure 6: Shop front after adding shop-exclusive items.

6 Assessment and Marking Criteria

This assignment assesses course learning objectives:

1. apply program constructs such as variables, selection, iteration and sub-routines,

2. apply basic object-oriented concepts such as classes, instances and methods,

3. read and analyse code written by others,

4. analyse a problem and design an algorithmic solution to the problem,

5. read and analyse a design and be able to translate the design into a working program, and

6. apply techniques for testing and debugging, and

7. design and implement simple GUIs.

6.1 Marking Breakdown

Undergraduate students will have 100 available marks, postgraduate students will have 125 available

marks. Such that; grade = 20 ×

mark

100

Your total mark will be made up of functionality marks and style marks. Functionality marks

are worth 75 marks for undergraduate and 100 marks for postgraduate students. Style marks are

worth the other 25 marks. Your style marks will be awarded as a proportion of your functionality

marks using the formula below:

12

final style mark = style mark ×

functionality mark

(

100, if postgraduate

75, otherwise

6.2 Functionality Marking

Table 3 outlines the breakdown of functionality marks for the assignment. As in assignment 1

and 2, your assignment will be put through a series of tests and your functionality mark will

be proportional to the number of weighted tests you pass. You will be given a subset of the

functionality tests before the due date for the assignment.

Your assignment will be tested on the functionality of gameplay features. The automated tests

will play the game and attempt to identify components of the game, how these components function

during gameplay will then be tested. Well before submission, run the functionality tests to

ensure components of your application can be identified. If the autograder is unable to identify

components, you will not receive marks, even if your assignment is functional. The tests

provided prior to submission will help you ensure that all components can be identified by the

autograder.

You also need to perform your own testing of your program to make sure that it meets all specifications

given in the assignment. Only relying on the provided tests is likely to result in your

program failing in some cases and you losing some functionality marks.

Your program must run in the Python interpreter (the IDLE environment). Partial solutions will

be marked, but if there are errors in your code that cause the interpreter to fail to execute your

program, you will get zero for functionality marks. If there is a part of your code that causes

the interpreter to fail, comment out the code so that the remainder can run. Your program must

run using the Python 3.10 interpreter. If it runs in another environment (e.g. Python 3.9, or

PyCharm) but not in the Python 3.10 interpreter, you will get zero for the functionality mark.

Feature Undergradute Postgraduate

Task 1 45 45

Window title 3 3

Game grid 20 20

Inventory 10 10

Stats view 10 10

Game end events 2 2

Task 2 30 30

Images 10 10

File menu 10 10

Controls 10 10

Task 3 0 25

Basic shop 0 15

Candy 0 10

Total 75 100

Table 3: Functionality marks breakdown.

13

6.3 Style Marking

The style of your assignment will be assessed by a tutor. The style mark will be out of 25.

The key consideration in marking your code style is whether the code is easy to understand.

There are several aspects of code style that contribute to how easy it is to understand code. In

this assignment, your code style will be assessed against the following criteria.

? Readability

– Program Structure: Layout of code makes it easier to read and follow its logic. This

includes using whitespace to highlight blocks of logic.

– Identifier Names: Variable, constant, function, class and method names clearly describe

what they represent in the program’s logic. Do not use Hungarian Notation for

identifiers.

? Documentation

– Inline Comments: All significant blocks of code should have a comment to explain how

the logic works. For a small method or function, the logic should usually be clear from

the code and docstring. For long or complex methods or functions, each logical block

should have an in-line comment describing its logic.

– Informative Docstrings: Every class, method and function should have a docstring that

summarises its purpose. This includes describing parameters and return values so that

others can understand how to use the method or function correctly.

? Code Design

– Single Instance of Logic: Blocks of code should not be duplicated in your program.

Any code that needs to be used multiple times should be implemented as a method or

function.

– Control Structures: Logic is structured simply and clearly through good use of control

structures (e.g. loops and conditional statements).

? Object-Oriented Program Structure

– Model View Controller: The GUI’s view and control logic is clearly separated from

the model. Model information stored in the controller and passed to the view when

required.

– Abstraction: Public interfaces of classes are simple and reusable. Enabling modular

and reusable components which abstract GUI details..

– Encapsulation: Classes are designed as independent modules with state and behaviour.

Methods only directly access the state of the object on which they were invoked. Methods

never update the state of another object.

– Inheritance: Subclasses extend the behaviour of their superclass without re-implementing

behaviour, or breaking the superclass behaviour or design. Abstract classes have been

used to effectively group shared behaviour amongst subclasses.

14

7 Assignment Submission

Your assignment must be submitted as a3.py via the assignment three submission link on Gradescope.

You should not submit any other files (e.g. maps, images, etc.). You do not need to

resubmit a2 solution.py or any other supplied files.

Late submission of the assignment will not be accepted. In the event of exceptional circumstances,

you may submit a request for an extension.

All requests for extension must be submitted on the UQ Application for Extension of Progressive

Assessment form: https://my.uq.edu.au/node/218/2 at least 48 hours prior to the

submission deadline.

8 Appendices

8.1 Appendix A: Permitted libraries.

You will need the following libraries to form a working solution:

1. tkinter and any of its submodules: You will need to import tkinter itself, as well as some of

its submodules. For example, to use a messagebox, you will need to explicitly import the

messagebox submodule from tkinter. If you do not explicitly import the submodules you

are using, your solution will likely work in IDLE, but nowhere else.

2. PIL

You may import the following libraries if you wish (but do not need them to create a working

solution):

1. math

2. typing

Use of any other libraries (including in-built libraries) is not permitted and will be penalized

by a deduction of up to 100% of the Assignment 3 grade.

15


版权所有:留学生编程辅导网 2020 All Rights Reserved 联系方式:QQ:99515681 微信:codinghelp 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。 站长地图

python代写
微信客服:codinghelp