COMP1100 Assignment 1, Semester 2 2024
In this assignment, you will build a Haskell program that uses the CodeWorld API to draw colourful shapes on the screen.
This assignment is worth 10% of your final grade.
Deadlines: Sunday 15 September, 2024, at 11:00pm Canberra time sharp. Note: Late submissions will not be marked unless you have an approved extension. Extensions can only be applied for through the Extension App. Students with Education Access Plans still need to use this App.
Required Knowledge
If you have finished the Week 3 lab, you should be able to attempt Task 1.
If you have finished the Week 4 lab, you should be able to attempt the majority of the assignment. Some parts require recursion over lists, which is covered in the Week 5 lab.
Overview of Tasks
Task 1: Helper Functions | 20 marks |
Task 2: Rendering Shapes | 45 Marks |
Task 3: Handling Events | 35 Marks |
Report | 0 marks |
Total | 100 marks |
Task 1 differs from the rest of the assignment in that you are able to ask for detailed help from tutors, peers, or other sources of assistance without this being considered plagiarism. This is to make sure that you have the help you need to get started on your first programming assignment. We recommend you attempt Task 1 on your own before asking for help, so that you have something to discuss. Help from tutors is best sought through drop-in consultations or the Ed Discussions forum because, during labs, tutors are usually busy helping with that week’s exercises. Usual academic misconduct standards apply to Tasks 2 and 3 of the assignment. You may also ask for help from tutors for these later tasks, but the help you will receive will be focused on conceptual understanding rather than details of the solutions.
Gitlab CI
You may notice a box in your repository on the Gitlab web view, looking something like this:
To help you ensure your submitted code is compiling, we’ve added a Continuous Integration (CI) script to this assignment. The CI will check that your code compiles, and run some tests on your code.
There’s three states your CI can be in:
- A red x, indicating your code is not compiling. This is something you should fix, as non-compiling code will not receive marks. You could fix this by either resolving the error, or by commenting out the erroring code to still submit it.
- An orange exclamation: this indicates that one of the tests in
tests/ShapesTest.hs
isn’t passing. This is to be expected at the start of the assignment, but once you have finished Task 1 of the assignment, this might indicate an issue with your code. - A green tick: this indicates that your code is compiling, and all the tests are passing.
Getting Started
-
Fork the assignment repository and create a project for it in VSCodium, following the same steps as in Lab 2. The assignment repository is at https://gitlab.cecs.anu.edu.au/comp1100/2024s2/2024s2studentfiles/comp1100-2024s2-asst1.
-
Add our version of the repository as a remote called
upstream
. This allows us to provide additional fixes in the case they are required. You do this by doing the following:- Go to the command palette in VSCode (or VSCodium) by pressing
Ctrl + Shift + p
- Type
git remote
- Click Git: Add Remote
- Enter
upstream
into the box for the remote name - Put the following URL as the remote url:
https://gitlab.cecs.anu.edu.au/comp1100/2024s2/2024s2studentfiles/comp1100-2024s2-asst1
.
- Go to the command palette in VSCode (or VSCodium) by pressing
Overview of the Repository
Most of your code will be written to Haskell files in the src/
directory. We are using the
Model-View-Controller
pattern to structure this assignment. Each file is called a module,
and we use modules to group related code together and separate
unrelated code.
Model.hs
The Model is a data type that describes the state of the running
program. The program will move to new states (new values of type
Model
) in response to user actions, as defined by the
Controller.
View.hs
The View turns the Model into something that can be shown on the
screen; in this project, that is the CodeWorld Picture
type.
Controller.hs
The Controller considers user input (and other events), along with the current Model, and uses that to decide what the new Model should be.
Other Files
-
tests/ShapesTest.hs
contains some unit tests - simple checks that can help to verify that Task 1 of your program is working correctly. You are not required to write your own tests for this assignment, but you might find it useful to do so. -
tests/Testing.hs
is a small testing library used bytests/ShapesTest.hs
. You are not required to understand it for this assignment. -
app/Main.hs
ties your functions together into the final program that runs. You are not required to understand it. -
comp1100-assignment1.cabal
andSetup.hs
tell the cabal build tool how to compile your assignment. You are not required to understand these files. We we will discuss how to use the compiler below. -
.gitignore
,.gitlab-ci.yml
, andDockerfile
are there to support our use of git, including continuous integration. You are not required to understand them.
Overview of Cabal
cabal
is the build tool for Haskell programs and libraries. It
provides several useful commands:
-
cabal v2-build
: Compile your assignment. Note that because of some code provided for you by us you will see some warnings about unused variables; you will fix these warnings during Task B, so may ignore them for Task A. -
cabal v2-run shapes
: Build your assignment (if necessary), and run theshapes
program. Note that you will need to enterCtrl-C
in your terminal to exit the program. -
cabal v2-repl comp1100-assignment1
: Run the GHCi interpreter over your project. This gives you the same ghci environment you use in labs, but with the assignment code loaded. Because you are working over multiple files, for modules other thanModel
you will have to tell the interpreter where to find the function you wish to test, by writing the module name, then dot, before the function name. For example,newColour Black
will not run, butController.newColour Black
will. The same applies to CodeWorld functions, e.g.drawingOf
will fail, butCodeWorld.drawingOf
will work (given an appropriate input). -
cabal v2-test
: Build and run the Task 1 tests.
You should execute these cabal commands in the top-level directory of your project (i.e., the directory you are in when you launch the VSCodium Terminal for your project). If you are in the wrong directory, consult [Lab 2](/courses/comp1100/labs/01/ on how to change your directory.
Interacting with the Program
You use a web browser to interact with the shapes
program that you
launched with cabal v2-run shapes
. Once you have completed the
assignment, it will respond to the following actions:
Action | Effect |
---|---|
Esc (key) |
Clear the canvas and all settings. |
1 /2 (key) |
Display the sample images. |
C (key) |
Change colour (of shape to draw). |
T (key) |
Change tool (type of shape to draw). |
Backspace (key) |
Remove the last added shape. |
Spacebar (key) |
When drawing a polygon, finish drawing the polygon, adding it to the canvas. Otherwise, nothing. |
D (key) |
Print the current Model to the terminal (useful for testing). |
Click-drag-release (mouse) | Used to draw various shapes. |
Click (mouse) | Used to draw various shapes. |
Task 1: Helper Functions (20 marks)
The easiest way to solve a large problem is often to break it apart
into smaller, easier problems. Programming is the same. In this task
you will write some helper functions that will make future tasks
easier. You can test your implementations by running cabal v2-test
.
The functions you need to write for this task are:
newColour
insrc/Controller.hs
. This function should return the next colour from our type ofColourChoice
s:
Argument | Result |
---|---|
Black |
Red |
Red |
Orange |
Orange |
Yellow |
Yellow |
Green |
Green |
Blue |
Blue |
Indigo |
Indigo |
Violet |
Violet |
White |
White |
Black |
-
newTool
insrc/Controller.hs
. This function implements tool-switching, in the following sequence:LineTool
->PolyTool
->SquareTool
->CircleTool
->ParallelogramTool
->ShieldTool
.-
This tool change should proceed regardless of whether the input tool is holding any information (for example,
LineTool
should change to aPolyTool
whether it is holdingNothing
, orJust p
for some pointp
. -
The new tool should not hold any information, so
PolyTool
should hold the empty list[]
, and allMaybe Point
s helf by other tools should beNothing
.
-
-
toolToLabel
insrc/View.hs
. This function should return instructions for the user on how to use eachTool
, exactly according to the following table:
Tool | Label |
---|---|
LineTool |
"Line: click-drag-release" |
PolyTool |
"Polygon: click 3+ times, then spacebar" |
SquareTool |
"Square: click-drag-release" |
CircleTool |
"Circle: click-drag-release" |
ParallelogramTool |
"Parallelogram: click-drag-release, then click" |
ShieldTool |
"Shield: click-drag-release" |
Hint 1: At the time this assignment is released, the course will have only
briefly covered lists. You do not need to manipulate lists to write the Task 1 functions
functions. Use the blank pattern _
where you wish to ignore the input, and use
[]
to refer to the empty list.
Hint 2: If anything is unclear, study the tests in test/ShapesTest.hs
.
Task 2: Rendering Shapes (45 marks)
In src/View.hs
, modelToPicture
converts your Model
type into a
CodeWorld Picture
, so that it can be displayed on the screen. It
currently does not work, because drawAllShapes
is
undefined
. In this task you will fill in that missing piece,
building up a function to convert the list of type [ShapeColoured]
from your
Model
into a Picture
. You can test these functions individually by
using cabal v2-repl comp1100-assignment1
, using CodeWorld.drawingOf
to show
small pictures on the screen.
You can also test everything as a whole
by launching the program with cabal v2-run shapes
and pressing the 1
and 2
keys to show the sample images. The functions you need to write for
this task are all in src/View.hs
:
-
colourChoiceToColour
: This function turns yourColourChoice
type from the Model into a CodeWorldColour
. You should check the CodeWorld documentation for information on colours. Most of yourColourChoice
s should map toColour
s in the obvious way, e.g. fromBlack
toblack
. ButIndigo
should have RGB (Red-Green-Blue) values0.45 0.35 1
andViolet
should have RGB values0.6 0 1
. -
drawShape
: This function turns your (uncoloured)Shape
type into a CodeWorldPicture
. You will need to consider the constructors forShape
individually, and work out the best way to turn each one into aPicture
. Here are some hints and instructions to help you along:-
CodeWorld has no function to draw a single line segment. It does have a function to draw a line made of multiple segments -
polyline
. It also has no function for squares, parallelograms, or shields, but it does have functions that can be made to draw these shapes. -
All shapes except
Line
s should be drawn as solid (filled)Picture
s. -
Many of CodeWorld’s functions draw individual shapes centred on the origin -
(0, 0)
. You will need to figure out how to slide (translate) the generatedPicture
so it shows up where it is supposed to go. Drawing diagrams will help. Theabs
function might also help - it computes the absolute value of its argument (i.e.,abs x == x
ifx >= 0
, andabs x == - x
otherwise). -
For
Square
s, the first and secondPoint
s define the corners of the first side; the second side should go from the secondPoint
at right angles clockwise from the first side. For example, if the secondPoint
is exactly to the right of the first, the second side of the square should run exactly top-to-bottom. Because users do not typically drag their mouse in exactly straight lines, the sides of the drawn square will typically not be at right angles to the x and y axes. Note also that because of the clockwise rule, aSquare
defined by two points will be drawn differently from aSquare
defined by those same points in the opposite order. -
Circle
s are defined via a ‘bounding square’ that is defined as withSquare
. The circle drawn is centred in the middle of this (invisible) square, and just big enough to touch the outside of this square. -
A
Parallelogram
is defined by threePoints
: the first two are the corners of the first side, as above, and then the second side connects the secondPoint
to the thirdPoint
. -
A
Shield
(technically, a ‘heater shield’) can be seen as built from three components. First, a rectangle, parallel to the x and y axes, is defined by taking the twoPoint
s as opposite corners. Second, a circular sector whose radius is the width of the rectangle and whose centre is the bottom left hand corner of the rectangle. Third, another sector of the same radius whose centre is the bottom right corner of the rectangle. These sectors should be extended below the rectangle just far enough to meet at a point below the rectangle. This picture is coloured to show you the three components:
-
-
drawShapeColoured
: This function should render theShape
and colour it using theColour
that corresponds to the givenColourChoice
. -
drawAllShapes
: This function should turn everyShapeColoured
in a list into a singlePicture
. You will need to recurse over the input list. If you have not yet completed Lab 5, you may want to work on other parts of the assignment and come back to this. In particular, we will treatShapeColoured
s to the left of the list as newer than those on the right of the list, and newer shapes should be drawn over the top of older ones. -
Here are the sample images (drawn by my children) for you to test your work against:
Task 3: Handling Events (35 marks)
It is now time to tackle user input. The function handleEvent
in
src/Controller.hs
is called by CodeWorld whenever something
interesting happens (like user input). This function is called with two arguments:
- The
Event
that just happened, and - The current
Model
at the time theEvent
happened.
handleEvent
then returns a new Model
for the program to use moving
forward.
(Aside: Elm is a functional programming language that uses a similar pattern to build front-end web applications that are compiled to JavaScript.)
Let’s trace a simple interaction. The user, starting with a blank canvas,
wants to draw a red line by clicking on the screen at coordinates
Model [] Black (LineTool Nothing)
-
The user presses “C” to change the colour from black to red:
Model [] Red (LineTool Nothing)
-
The user presses the mouse button at
, changing the state toModel [] Red (LineTool (Just (1.0,1.0)))
-
The user releases the mouse button at
, changing the state toModel Red [(Line (1.0,1.0) (2.0,2.0),Red)] (LineTool Nothing)
Note that the Tool
and the ColourChoice
do not reset to the default values
after a shape has been drawn. However, the Maybe Point
inside the tool
should revert to Nothing
.
handleEvent
is written for you, calling different helper functions depending on whether
the event is a mouse pointer press, a mouse pointer release, or a key press. It is your
job to write those helper functions.
Task 3.1: Handling Mouse Input
CodeWorld provides a few different event constructors for mouse input,
but the ones we are interested in here are PointerPress
for when the user
clicks, and PointerRelease
for when the user releases the mouse
button. Both events happen at one particular Point
, which is passed to a helper
function.
For most shapes we consider, when a PointerPress
event arrives, you will need to store it
in the current Tool
. Usually, you store in a Maybe Point
argument, but for PolyTool
,
you should add it to the list of Point
s.
For most shapes, when a PointerRelease
event arrives, this means the end of a
click-drag-release action. You should construct the appropriate ShapeColoured
and add
it to the Model
. For example, a Square
might be added to the Model
whose first
Point
is the place the user clicked, and second Point
is where they released.
You should then remove all information from the current Tool
so that future shapes draw
properly too.
The approach for ParallelogramTool
will be a bit different: the user click-drag-releases
to define the first two corners, then clicks again to define the third corner. Therefore
PointerPress
should behave differently depending on whether the user is currently
defining the first or third corner, and PointerRelease
similarly either defines the
second corner, or should be ignored, depending on the state of the tool.
Once you have finished this task for normal input, you may also want to consider
how your program will behave on unexpected input. For example, what should your program
do if it receives two consecutive PointerPress
inputs without a PointerRelease
between them?
Task 3.2: Handling Key Presses
To handle keyboard input, CodeWorld provides a KeyPress
event. The name of the key press
is passed to the handleKeyPress
helper function as a String
. We have already written
part of this function because we have
implemented some keyboard functionality already. In the “Interacting with the Program” section, we listed the full set of keyboard commands that
your program will respond to. You need to implement the missing
functionality for these keys:
Key | Effect |
---|---|
C |
Change colour (of shape to draw) |
T |
Change tool (type of shape to draw) |
Backspace |
Remove the last added shape |
Spacebar |
Finish drawing a polygon, adding it to the canvas. |
If you have made it this far, you should not need to write a lot of code to implement these. A few hints:
- Think back to Task 1.
Backspace
should delete the newest shape, which is on the left of the list.Backspace
with no shapes drawn should not crash the program…- … nor should any other unexpected input. Try to test some unexpected cases.
Report (0 marks)
You need to add a short report only if you need to cite any sources that helped with the completion of your assignment. In particular, use these instructions if you need to acknowledge the use of generative AI.
Your report, if you submit one, must be in PDF format, located at the root of
your assignment repository on GitLab and named Report.pdf
. Otherwise, it
might not be noticed by your marker. You should
double-check on GitLab that the report has been added to your repository correctly.
No formal word limit is applied for reports but it should be very short.
The report must begin with the following items:
- Your name
- Your university ID
- Your laboratory time and tutor
Communicating
Do not post your code publicly, even for Task 1, on Ed Discussions or via any other means. Posts on Ed Discussions can trigger emails to students, depending on how they have configured their notifications, so if by mistake you post your code publicly, others will have access to your code and you may be held responsible for plagiarism.
Once again, and we cannot stress this enough: do not post your code publicly . If you need help with your code, post it privately to the instructors.
When brainstorming with your friends, do not view each others’ code for anything other than Task 1. There might be pressure from your friends, but this is for both your and their benefit. Anything that hints at plagiarism will be investigated and there may be serious consequences.
Sharing concepts and sketches is perfectly fine, but sharing should stop before you risk handing in suspiciously similar solutions.
The use of generative AI tools (e.g. ChatGPT) is permitted in this course, with appropriate citation.
Course staff will not look at assignment code unless it is posted privately in Ed Discussions, or shared in a drop-in consultation.
Course staff will typically give assignment assistance by asking questions, directing you to relevant exercises from the labs, or definitions and examples from the lectures. They will not give direct instructions on how to edit your Task 2 and 3 code, but they will give you more generous help for questions related to Task 1.
Submission Checklist
Once you have finished your assignment, and preferably 24 hours prior to the deadline, you should make sure that:
- You have fully read and understand the entire assignment specification.
- Your work has been pushed to GitLab. You should check this using your browser by viewing your repository at https://gitlab.cecs.anu.edu.au/uXXXXXXX/comp1100-2024s2-asst1 , where XXXXXXX is replaced by your university ID. It is your responsibility to confirm that your submission, including for your report, has been successful.
- Your program compiles and runs, including the
cabal v2-test
test suite. Your mark will mostly reflect the performance of your code, and code that does not compile and run will therefore receive extremely low marks. - Your program works on the lab machines - if the program does not work on the lab machines, it might fail tests used by the instructors.
- You have proof-read and spell-checked your report.
- The report, if submitted, is in PDF format, located at the root of your project on
GitLab and named
Report.pdf
. That capitalR
is important - Linux uses a case-sensitive file system. Otherwise, it might not be noticed by your marker. Check this on Gitlab as in some situations operating systems may change the formatting of filenames.