COMP1100 Assignment 1, Semester 2 2023
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:
- Part A: Thursday 24 August, at 11:00pm Canberra time sharp
- Part B: Friday 8 September, at 11:00pm Canberra time sharp
- Note: Late submissions will not be marked unless you have an approved extension.
Indicative marks and feedback for Part A will be returned in Week 6.
Required Knowledge
If you have finished the Week 3 lab, you should be able to complete Part A.
If you have finished the Week 4 lab, you should be able to complete the majority of the assignment. Some parts require recursion over lists, which is covered in the Week 5 lab.
Overview of Tasks
Tasks | Marks |
---|---|
Task 1: Helper Functions | 20 marks |
Task 2: Rendering Shapes | 35 Marks |
Task 3: Handling Events | 30 Marks |
Technical Report | 15 marks |
Total | 100 marks |
Part A of the assignment requires you to complete Task 1.
Part B of the assignment requires you to complete all assigned tasks.
As you complete each task (or even each function within a task), once your code compiles without errors, you should commit and push your work with a sensible commit message. We recommend committing your work as you go, and pushing regularly. Do not wait until you are finished to commit and push for the first time, as you run the risk of missing the deadline and having nothing submitted. An unfinished assignment is worth more than no assignment.”
The purpose of Part A is to give you an opportunity to collect feedback on your code and your progress in the course, and for us to give you an indicative mark for your work so far. This will be returned to you before the census date. Part A will be re-marked alongside your Part B submission, giving you a final mark for the assignment.
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/2023s2/studentfiles/asst1-1100_s2_2023.
-
Add our version of the repository as a remote called
upstream
. This allows us to provide additional fixes in the unlikely case that 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/2023s2/studentfiles/asst1-1100_s2_2023.git
.
- 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.
This file contains definitions of new data types that will be used
throughout the assignment. It is worth making sure that you understand
each of these new data types.
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 verify small parts of your program are working correctly. You are not required to write 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
tells the cabal build tool how to build your assignment. You are not required to understand this file, and we will discuss how to use cabal below. -
Setup.hs
tells cabal that this is a normal package with no unusual build steps. Some complex packages (that we won’t see in this course) need to put more complex code here. You are not required to understand it.
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. (Aside: REPL is the name for interactive sessions like GHCi - it stands for read-eval-print loop. Many modern languages have REPLs.) -
cabal v2-test
: Build and run the tests. Tests will abort on the first failure, or the first call to a function that is stillundefined
.
You should execute these cabal commands in the top-level directory of your
project, e.g. ~/comp1100/assignments/Assignment1
(i.e., the directory you are in when you
launch the VSCodium Terminal for your project).
Overview of 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. |
S (key) |
Display a sample image. |
C (key) |
Change colour (of shape to draw). |
T (key) |
Change tool (type of shape to draw). |
Backspace /Delete (key) |
Remove the last added shape. |
Spacebar (key) |
When drawing a polygon, finish drawing the polygon, adding it to the canvas if it has at least three vertices. Otherwise, nothing. |
↑ (key) |
Rotate semicircles anti-clockwise π/20 radians. |
↑ (key) |
Add 0.1 to the stretch of an ellipse in the direction perpendicular to the initial line. |
↓ (key) |
Rotate semicircles clockwise π/20 radians. |
↓ (key) |
Deduct 0.1 to the stretch of an ellipse in the direction perpendicular to the initial line. |
← (key) |
Deduct 0.1 to the stretch of an ellipse in the direction of the initial line. |
→ (key) |
Add 0.1 to the stretch of an ellipse in the direction of the initial line. |
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:
toolToLabel
insrc/View.hs
. This function should return instructions for the user on how to use eachTool
(defined insrc/Model.hs
), according to the following table:
Tool | Label |
---|---|
LineTool |
"Line: click-drag-release" |
RectangleTool |
"Rectangle: click-drag-release the diagonal" |
PolyTool |
"Polygon: click 3 or more times then spacebar" |
SemiCircleTool |
"SemiCircle: click-drag-release the diameter" |
EllipseTool |
"Ellipse: click-drag-release the diameter, then use arrows to stretch" |
Note: At the time this assignment is released, the course will have only
briefly covered lists. You do not need to manipulate lists to write
toolToLabel
; you can use a blank pattern (_
) to ignore them.
nextColour
insrc/Controller.hs
The program displays 7 distinct colours, labeled from ‘Red’ to ‘Blue’, transitioning gradually from the colour red to blue. The functionnextColour
insrc/Controller.hs
should return the next colour in our set ofColourName
s (defined insrc/Model.hs
):
Argument | Result |
---|---|
Red |
MostlyRed |
MostlyRed |
KindaRed |
KindaRed |
RedAndBlue |
RedAndBlue |
KindaBlue |
KindaBlue |
MostlyBlue |
MostlyBlue |
Blue |
Blue |
Red |
-
nextTool
insrc/Controller.hs
. This function implements tool-switching, but should not changeTool
if the user is halfway through an operation:-
If the tool is not holding a point (that is, a
PolyTool
holding the empty list[]
, or another tool whose first argument isNothing
), select the next tool in the following sequence:LineTool
->RectangleTool
->PolyTool
->SemiCircleTool
->EllipseTool
->LineTool
. The parallel stretch and the perpendicular stretch of the ellipse should both be initialised to1.0
. The rotation of the semicircle should be initialised to0.0
. -
If there is a
Point
stored in the given tool (because it is holding aJust
value or the list inPolyTool
is non-empty), return the argument unchanged. -
If this is unclear, study the
nextToolTests
intest/ShapesTest.hs
.
-
Note: At the time this assignment is released, the course will have
only briefly covered lists. You can write the PolyTool
case for
nextTool
without using list recursion. Use []
to match an empty
list. In a subsequent case, give the entire list a name like points
to match any nonempty list (or find a way to use the _
pattern!).
Part A ends here.
Submitting Part A
Your submission for Part A should include implementations of
toolToLabel
, nextColour
, and nextTool
that
compile without warnings and pass the tests run by cabal v2-test
.
You are welcome to continue working on Part B of your assignment
and committing and pushing changes, so long as your code continues
to compile without errors and the tests continue to pass.
Part B begins…
Task 2: Rendering Shapes (35 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 colourShapesToPicture
is
undefined
. In this task you will fill in that missing piece,
building up a function to convert the [ShapeColour]
from your
Model
into a Picture
. You can test these functions individually by
using cabal v2-repl comp1100-assignment1
, using drawingOf
to show
small pictures on the screen.
If you wanted to test functions from View.hs
e.g. if your prompt shows ghci>
,
you can use ghci commands to load the specific module i.e. :l View
which should change the ghci prompt accordingly.
You can also test everything as a whole
by launching the program with cabal v2-run shapes
and pressing the S
key to show the sample image. The functions you need to write for
this task are all in src/View.hs
:
-
colourNameToColour
: This function turns yourColourName
type from the model into a CodeWorldColour
. You should check the CodeWorld documentation for information on colours. There are several ways to mix red and blue. (note: This will require you to check the codeworld documentation, rather than rely on what has already been covered in labs).- We do not have a specific expectation of what your colours should look like, as long as each colour is distinct, and they change gradually from red to blue.
- If you have trouble distinguishing red and blue, please get in touch: we can agree on two different colours and provide suitable example images.
-
shapeToPicture
: This function turns yourShape
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 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
. -
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). -
Rectangles, Polygons, and Ellipses should be drawn as solid (filled)
Picture
s. -
Our rectangles consist of two horizontal line segments and two vertical line segments (so they are not tilted). They are characterised by two points, which give the diagonal of the rectangle.
- In this assignment, by a ‘SemiCircle’ we mean a circle of which only
two quarters are coloured. Here are some remarks that may be helpful.
-
A semiCircle looks like in the following picture:
-
SemiCircles are defined by two points, which give the diameter of the circle, and a value indicating the rotation. They can be rotated in steps of
π/20
by using the up and down arrows on the keyboard. The semicircle in the picture above has a diameter given by the points(1,2)
and(3,2)
, and no rotation. A semicircle with the same diameter which has been rotated by two steps ofπ/20
looks as follows: -
Hint: it may be useful to combine pictures to create a SemiCircle.
-
-
Our ellipses are defined by first drawing a circle and then stretching it using the arrows on the keyboard.
-
The circle is defined by two points, which give the diameter.
-
The left and right arrows,
←
and→
, stretch the circle in the direction of the diameter. -
The up and down arrows,
↑
and↓
, stretch the circle in the direction perpendicular to the diameter. -
The following ellipse is drawn by using a diameter going from
(2,2)
to(4,2)
, stretched in the direction of the radius by2
(by pressing→
ten times), and not stretched in the direction perpedicular to the radius. -
As another example, here is an ellipse obtained by drawing the diameter from
(2,2)
to(3,3)
, stretched in the direction of the radius by2
(by pressing→
ten times), and stretched perpendicular to the radius by0.5
(by pressing↓
five times). -
Hint: you may find the function
scaled
useful here.
-
-
-
colourShapeToPicture
: This function should render theShape
and colour it using the CodeWorldColour
that corresponds to the givenColourName
. -
colourShapesToPicture
: This function should turn everyShapeColour
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 should draw older shapes first, and newer shapes last, so that newer shapes overlay older ones. -
Here is the sample image for you to test your work against:
Task 3: Handling Events (30 marks)
It is now time to tackle handleEvent
in
src/Controller.hs
. CodeWorld calls this function whenever something
interesting happens (like a key press, a pointer press, or a
pointer release). 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. If the user starts at a blank
canvas and wants to draw a kinda red
line by clicking on the screen at coordinates (1,1)
and
releasing the mouse at coordinates (2,2)
. starting at a blank
canvas, the state would transition as follows. Starting with the
initial model:
-
Model [] (LineTool Nothing) Red
-
The user presses “C” to change the colour from red to kinda red:
Model [] (LineTool Nothing) KindaRed
-
The user presses the mouse button at
(1,1)
changing the state toModel [] (LineTool (Just (1.0,1.0))) KindaRed
-
The user releases the mouse button at
(2,2)
changing the state toModel [(Line (1.0,1.0) (2.0,2.0),KindaRed)] (LineTool Nothing) KindaRed
Note that the Tool
and the ColourName
do not reset to the default values
after a shape has been drawn. However, the Maybe Point
inside the tool
should revert to Nothing
.
Task 3.1: Handling Mouse Input
CodeWorld provides a few different event constructors for mouse input,
but the ones we’re interested in here are PointerPress
for when the user
clicks, and PointerRelease
for when the user releases the mouse
button.
When a PointerPress
event arrives, you will need to store it in the
current Tool
. For everything except PolyTool
, you will store it
in the Maybe Point
argument. For PolyTool
, you will add it to
the list of vertices.
When a PointerRelease
event arrives, we can ignore it for
PolyTool
, as we will be finishing polygons using
the spacebar in Task 3.2. For almost everything else, a PointerRelease
will
mean the end of a click-drag-release action, so you should construct the appropriate
shape and add it to the [ShapeColour]
in the Model
. You should also
remove the starting point from the current Tool
, so that future
shapes draw properly too.
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. This
is already present in the assignment skeleton, because we have
implemented some keyboard functionality already. In the “Overview of
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 (key) |
Change colour (of shape to draw). |
T (key) |
Change tool (type of shape to draw). |
Backspace /Delete (key) |
Remove the last added shape. |
Spacebar (key) |
When drawing a polygon, finish drawing the polygon, adding it to the canvas if it has at least three vertices. Otherwise, nothing. |
↑ (key) |
Rotate semicircles anti-clockwise π/20 radians. |
↑ (key) |
Add 0.1 to the stretch of an ellipse in the direction perpendicular to the initial line. |
↓ (key) |
Rotate semicircles clockwise π/20 radians. |
↓ (key) |
Deduct 0.1 to the stretch of an ellipse in the direction perpendicular to the initial line. |
← (key) |
Deduct 0.1 to the stretch of an ellipse in the direction of the initial line. |
→ (key) |
Add 0.1 to the stretch of an ellipse in the direction of the initial line. |
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. |
If the current tool is SemiCircleTool
, pressing ↑ or ↓ should adjust the rotation of the semicircle to be drawn.
If the current tool is EllipseTool
, pressing ← or → should adjust the stretch in the direction
of the diameter, and pressing ↑ or ↓ should adjust the stretch of the ellipse to be drawn
perpendicular to the diameter.
If the last drawn shape is a semicircle or an ellipse, and a new drawing
is not yet started (so the Tool
holds an empty list []
or Nothing
),
then the arrows should adjust the rotation of that shape
(in the case of a semicircle), or the stretch of that shape
(in case of an ellipse).
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
/Delete
with no shapes drawn should not crash the program. -
Nor should any other unexpected input. Try to test some ``unexpected’’ cases.
Technical Report: 15 marks
Description | Mark |
---|---|
Documentation (what you did) | 4 |
Reflection (why you did it) | 4 |
Testing (how you tested) | 4 |
Style (how the report presents) | 3 |
TOTAL | 15 |
You should write a concise technical report explaining your design choices in implementing your program. The maximum word count is 1000. This is a limit, not a quota; concise presentation is a virtue.
Once again: These are not required word counts. They are the maximum number of words that your marker will read. If you can do it in fewer words without compromising the presentation, please do so.
Your report must be in PDF format, located at the root of your
assignment repository on GitLab and named Report.pdf
. Otherwise, it
may not be marked, or will be marked but with a penalty. You should
double-check on GitLab that this is typed correctly.
The report must have a title page with the following items:
-
Your name
-
Your laboratory time and tutor
-
Your university ID
Content and Structure
Your audience is the tutors and lecturers, who are proficient at programming and understand most concepts. Therefore you should not, for example, waste words describing the syntax of Haskell or how recursion works. After reading your technical report, the reader should thoroughly understand what problem your program is trying to solve, the reasons behind major design choices in it, as well as how it was tested. Your report should give a broad overview of your program, but focus on the specifics of what you did and why.
Remember that the tutors have access to the above assignment specification, and if your report only contains details from it then you will only receive minimal marks. Below is an potential outline for the structure of your report and some things you might discuss in it.
Introduction
If you wish to do so you can write an introduction. In it, give:
-
A brief overview of your program:
-
how it works; and
-
what it is designed to do.
-
-
If you have changed the way the controls work, or added something that may make your program behave unexpectedly, then it would be worth making a note of it here.
This section is particularly relevant to more complicated programs.
Analysis of your Program
The purpose of this section is to describe your program to the reader, both in detail and at a high level.
Talk about what features your program actually has. We know what we asked for (the features in this document!), but what does your program actually let a user do? How does your program work as a whole?
How does it achieve this? Let us know how each individual function works and how they work together to solve particular design goals.
As an example, you might have used a number of helper functions to achieve a particular design goal of drawing semicircles. If so, tell us what these functions are, what they do, and how they compose or otherwise work together to achieve this goal.
A successful report will demonstrate conceptional understanding of all relevant functions, and depict a holistic view of program structure through discussion of what it is and how it works.
Rationale and Reflection
The purpose of this section is to describe the design decisions you made while writing the program, to the reader.
Tell us the reasoning behind the choices you detailed above. Tell us the assumptions you made about user behaviour. Why did you solve the problems the way you did? Why did you write the functions you wrote? Did you make any other assumptions?
For example:
“I implemented the checkFirst
helper function after
reading this blog post (citing the post as a reference),
claiming that users of quadrant based drawing programs virtually always draw
their first shape in the top-right quadrant. Deciding to use this as my base
assumption for user-behaviour, I decided to save on
quadrant-dependent calculation of trigonometric ratios by always assuming the
first shape is drawn in this quadrant. This in turn meant I needed a function
to check if a shape was the first one drawn.”
This is a critical reflection not a personal one. You’re explaining the justification and reasoning behind the choices you made.
A successful report will give a thorough explanation of the process followed to reach a final design, including relevant reasoning and assumptions / influences.
Testing
The purpose of this section is to give the reader confidence that your program has been thoroughly tested.
Tell us how you tested the program as a whole to ensure correctness. Tell us in detail how you tested individual functions to ensure correctness.
For example:
“I drew all possible directions of the diameter on the coordinate plane, and used the up/down and left/right arrow keys to ensure that the ellipse, was correctly stretching along and perpendicular to the drawn diameter.”
A successful report will demonstrate evidence of a process that checked most, if not all, of the relevant parts of the program through testing. Such a report would combine this with some discussion of why these testing results prove or justify program correctness.
Style
A successful report should have excellent structure, writing style, and formatting. Write professionally, use diagrams where appropriate but not otherwise, and ensure your report has correct grammar and spelling.
Things to avoid in a technical report
-
Line by line explanations of large portions of code. (If you want to include a specific line of code, be sure to format as described in the “Format” section below.)
-
Pictures of code or your IDE.
-
Content that is not your own, unless cited.
-
Grammatical errors or misspellings. Proof-read it before submission.
-
Informal language - a technical report is a professional document, and as such should avoid things such as:
-
Unnecessary abbreviations (atm, wrt, ps, and so on), emojis, and emoticons; and
-
Stories / recounts of events not relevant to the development of the program.
-
-
Irrelevant diagrams, graphs, and charts. Unnecessary elements will distract from the important content. Keep it succinct and focused.
If you need additional help with report writing, ANU Academic Skills have resources to help.
Format
You are not required to follow any specific style guide (such as APA or Harvard). However, here are some tips which will make your report more pleasant to read, and make more sense to someone with a computer science background.
-
Colours should be kept minimal. If you need to use colour, make sure it is absolutely necessary.
-
If you are using graphics, make sure they are vector graphics (that stay sharp even as the reader zooms in on them).
-
Any code, including type/function/module names or file names, that appears in your document should have a mono-spaced font (such as Consolas, Courier New, Lucida Console, or Monaco).
-
Other text should be set in serif fonts (popular choices are Times, Palatino, Sabon, Minion, or Caslon).
-
When available, automatic ligatures should be activated.
-
Do not use underscore to highlight your text.
-
Text should be at least 1.5 spaced.
Communicating
Do not post your code publicly, either on Ed or via other forums. Posts on Ed trigger emails to all students, 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 share code. There might be pressure from your friends, but this is for both your and their benefit. Anything that smells of 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.
Course staff will not look at assignment code unless it is posted privately in Ed, or shared in a drop-in consultation.
Course staff will typically give assistance by asking questions, directing you to relevant exercises from the labs, or definitions and examples from the lectures.
Before the assignment is due, course staff will not give individual tips on writing functions for the assignment or how your code can be improved. We will help you get unstuck by asking questions and pointing you to relevant lecture and lab material. You will receive feedback on you work when marks are released.
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/asst1-1100_s2_2023 , 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. (It is important to make sure that your program compiles, as non-compiling programs lose 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 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 may not be marked. Check this on Gitlab as the full filename may not always appear in your document.