0% found this document useful (0 votes)
4 views

HASKELL Programming Task

The document outlines a programming task involving the board game Black Box, where players deduce the location of hidden atoms in a triangular grid by firing rays. It describes three tasks: calculating ray interactions with the grid, finding hidden atoms given interactions, and translating lambda expressions into continuation-passing style. The document also includes starting code and examples for implementing the required functions in Haskell.

Uploaded by

hassandmrw
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

HASKELL Programming Task

The document outlines a programming task involving the board game Black Box, where players deduce the location of hidden atoms in a triangular grid by firing rays. It describes three tasks: calculating ray interactions with the grid, finding hidden atoms given interactions, and translating lambda expressions into continuation-passing style. The document also includes starting code and examples for implementing the required functions in Haskell.

Uploaded by

hassandmrw
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 12

HASKELL Programming Task:

TASK1:

The board game Black Box consists of an N x N non-empty square grid in which
are hidden a given number of “atoms”. The player tries to determine the
location of the atoms by firing a “ray” in to the grid from one of its edges. The
ray will potentially deflect or reflect by the atoms and may exit the grid at one
of the edges. The aim of the game is to deduce the location of the atoms using
the least number of rays. For Challenges 1 and 2 we will consider a variation of
this game in which the board consists of a triangular grid of equilateral triangles
arranged so:

Similar to the square grid version we hide atoms in this grid and fire rays in to
the grid to deduce their location by looking at the exit points for each ray.
Atoms are filled equilateral triangles and rays will travel in straight lines directly
through the grid unless disturbed by the presence of an atom. A ray that strikes
an atom is reflected so that its angle of incidence (always 30˚) equals its angle
of reflection (always 30˚) so that total internal reflection is 60˚.
In order to specify the location of both the atoms and the positions at which we
fire rays into and detect rays exiting from the grid we need to establish a co-
ordinate system. For atoms we simply use, counting from top-down and left to
right, the row number of the atom, along with the offset in to that row. Counting
begins at 1. For rays, we identify the "East", "West" and "South" edges of the
whole figure and count edges of each small triangle along those edges. Again,
counting is top-down and left to right and counting begins at 1. Because rays
can be fired in at one of two directions at each edge, we also use "Left" and
"Right" to identify this direction. This indicates whether the ray enters from and
exits to the left or right of the edge midpoint when facing the edge. The
diagram below indicates the naming scheme for ray edges and directions.
-
Given our naming scheme we can present examples of atoms in a grid and the
example paths that rays make take. Bear in mind that a ray may be reflected
more than once before it exists the grid.
First we have an example of a ray not being disturbed by an atom:

Next we have an example of a ray being reflected a single time:

In some circumstances the ray may not even enter the grid and be immediately
reflected :
Finally, we see a ray that is reflected multiple times before exiting the grid :

In this exercise we will write a Haskell function that, given the size of the grid
and the location of atoms within a grid, then the function will calculate all
possible ray firings. That is, for every ray edge and direction, we calculate the
exit location for that ray.
The data types that we will use are as follows :
data EdgeDir = L | R deriving (Eq,Show,Ord)
data Face = East | West | South deriving (Eq,Show,Ord)
data EdgePoint = EP Face Int EdgeDir deriving (Eq,Show,Ord)
type Atom = (Int,Int)
Write the function
calcInteractions :: Int - > [ Atom ] - > [ ( EdgePoint , EdgePoint ) ]
that given the size of the grid and location of a number of atoms, returns the
list of pairs of entry and exit points for every possible ray. The order in which
you return these is not important. You may assume that all atom locations are
valid locations in the grid.
For example:
Test Result

calcInteractions 6 [ [(EP East 1 L,EP West 1 R),(EP East 1 R,EP South 1


(2,3) , (4,3) , L),(EP East 2 L,EP East 2 R),(EP East 2 R,EP East 2
(4,6) , (5,5) ] L),(EP East 3 L,EP West 3 R),(EP East 3 R,EP East 5
L),(EP East 4 L,EP South 6 R),(EP East 4 R,EP South 4
L),(EP East 5 L,EP East 3 R),(EP East 5 R,EP South 5
L),(EP East 6 L,EP West 6 R),(EP East 6 R,EP South 6
L),(EP West 1 L,EP West 2 R),(EP West 1 R,EP East 1
L),(EP West 2 L,EP South 5 R),(EP West 2 R,EP West 1
L),(EP West 3 L,EP West 4 R),(EP West 3 R,EP East 3
L),(EP West 4 L,EP South 3 R),(EP West 4 R,EP West 3
L),(EP West 5 L,EP South 2 R),(EP West 5 R,EP South 2
L),(EP West 6 L,EP South 1 R),(EP West 6 R,EP East 6
L),(EP South 1 L,EP East 1 R),(EP South 1 R,EP West 6
L),(EP South 2 L,EP West 5 R),(EP South 2 R,EP West 5
L),(EP South 3 L,EP South 4 R),(EP South 3 R,EP West
4 L),(EP South 4 L,EP East 4 R),(EP South 4 R,EP
South 3 L),(EP South 5 L,EP East 5 R),(EP South 5
R,EP West 2 L),(EP South 6 L,EP East 6 R),(EP South 6
R,EP East 4 L)]
Answer:(penalty regime: 0 %)

TASK 2:

In this challenge we continue with the black box game as defined in Challenge
One. We use the same data types and representation of the grid.
For this challenge however we will be given a complete list of interactions with
the grid and be told the number of atoms hidden in the grid. Your task is to
write a function that finds the hidden atoms. Write a function solveTBB :: Int -
> [ (EdgePoint,EdgePoint) ] -> [Atom] that does this.
You may assume that the given list of interactions contains an entry for every
edge point of the grid. You may also return any valid solution (not all list of
interactions identify unique atom placements). By valid, we mean that if we
apply (a correct implementation of) the calcInteractions function from
Challenge One to the returned solution then this will return the given list of
interactions.
Your solution should be able to solve grids containing 4 to 5 atoms up to size 10
within approx 30 seconds. The tests have a timeout of 45 seconds.
For example:

Test Result
solveTBB 4 [(EP East 1 L,EP West 1 R),(EP East 1 [(4,3),(5,1),(6,7),
R,EP East 5 L),(EP East 2 L,EP West 2 R),(EP East (8,12)]
2 R,EP East 4 L),(EP East 3 L,EP West 3 R),(EP
East 3 R,EP South 3 L),(EP East 4 L,EP East 2 R),
(EP East 4 R,EP East 6 L),(EP East 5 L,EP East 1
R),(EP East 5 R,EP South 5 L),(EP East 6 L,EP East
4 R),(EP East 6 R,EP West 2 L),(EP East 7 L,EP
West 7 R),(EP East 7 R,EP South 7 L),(EP East 8
L,EP South 7 R),(EP East 8 R,EP South 8 L),(EP
West 1 L,EP South 8 R),(EP West 1 R,EP East 1 L),
(EP West 2 L,EP East 6 R),(EP West 2 R,EP East 2
L),(EP West 3 L,EP West 4 R),(EP West 3 R,EP East
3 L),(EP West 4 L,EP South 5 R),(EP West 4 R,EP
West 3 L),(EP West 5 L,EP West 5 R),(EP West 5
R,EP West 5 L),(EP West 6 L,EP South 3 R),(EP West
6 R,EP South 2 L),(EP West 7 L,EP South 2 R),(EP
West 7 R,EP East 7 L),(EP West 8 L,EP South 1 R),
(EP West 8 R,EP South 6 L),(EP South 1 L,EP South
4 R),(EP South 1 R,EP West 8 L),(EP South 2 L,EP
West 6 R),(EP South 2 R,EP West 7 L),(EP South 3
L,EP East 3 R),(EP South 3 R,EP West 6 L),(EP
South 4 L,EP South 6 R),(EP South 4 R,EP South 1
L),(EP South 5 L,EP East 5 R),(EP South 5 R,EP
West 4 L),(EP South 6 L,EP West 8 R),(EP South 6
R,EP South 4 L),(EP South 7 L,EP East 7 R),(EP
South 7 R,EP East 8 L),(EP South 8 L,EP East 8 R),
(EP South 8 R,EP West 1 L)]
Answer:(penalty regime: 0 %)

This is the starting code

-- Your Imports Here

-- DO NOT MODIFY THESE DATA TYPES


data EdgeDir = L| R deriving (Eq,Show,Ord)
data Face = East | West | South deriving (Eq,Show,Ord)
data EdgePoint = EP Face Int EdgeDir deriving (Eq,Show,Ord)
type Atom = (Int,Int)
-----------------------

TASK 3:
Continuation-Passing Style (CPS) is a form of writing lambda calculus terms in
which the control of evaluation is encoded within a term as a continuation and
these are passed explicitly within functions. Think of a continuation as a function
that represents the next bit of computation to perform. You can read about this
style in many online tutorials and a good starting point is the Wikipedia page
at: https://en.wikipedia.org/wiki/Continuation-passing_style
A common translation [[E]] of a lambda calculus term to another lambda
calculus term in continuation passing style is as follows:
[[ x ]] = λ κ → (κ x)
[[ λx → E ]] = λκ → (κ λx → [[ E ]])
[[ (E1 E2) ]] = λκ → ( [[ E1 ]] λf → ( [[ E2 ]] λe → (f e κ) ) )
where the variable names k. f and e are chosen fresh so as to not capture any of
the variables of E.
We can easily extend the above translation to the lambda calculus with macros
language as described in Challenge 3 using
[[ def X = E in ME ]] = def X = [[ E ]] in [[ ME ]]
and
[[ X ]] = X
Write a function cpsTransform :: LamMacroExpr -> LamMacroExpr that translates a
lambda expression with macros in to its corresponding CPS translated form
according to the above translation rules. Take care not to capture any free
variables in the given expression during translation. Your solution may use
different variable names from those given in the example outputs. Any alpha-
equivalent answer will be acceptable.
For example:

Test Result

cpsTransform (LamDef [] LamDef [] (LamAbs 0 (LamApp (LamAbs 0 (LamApp


(LamApp (LamVar 1) (LamVar 0) (LamVar 1))) (LamAbs 1 (LamApp (LamAbs
(LamVar 2))) 0 (LamApp (LamVar 0) (LamVar 2))) (LamAbs 2
(LamApp (LamApp (LamVar 1) (LamVar 2)) (LamVar
0)))))))

cpsTransform (LamDef LamDef [("F",LamAbs 0 (LamApp (LamVar 0) (LamAbs


[ ("F", LamAbs 1 (LamVar 0 (LamAbs 1 (LamApp (LamVar 1) (LamVar 0))))))]
1)) ] (LamVar 2) ) (LamAbs 0 (LamApp (LamVar 0) (LamVar 2)))

cpsTransform (LamDef LamDef [("F",LamAbs 0 (LamApp (LamVar 0) (LamAbs


[ ("F", LamAbs 1 (LamVar 0 (LamAbs 1 (LamApp (LamVar 1) (LamVar 0))))))]
1)) ] (LamMacro "F") ) (LamMacro "F")
Test Result

cpsTransform (LamDef LamDef [("F",LamAbs 0 (LamApp (LamVar 0) (LamAbs


[ ("F", LamAbs 1 (LamVar 0 (LamAbs 1 (LamApp (LamVar 1) (LamVar 0))))))]
1)) ] (LamApp (LamMacro (LamAbs 0 (LamApp (LamMacro "F") (LamAbs 1
"F") (LamMacro "F"))) (LamApp (LamMacro "F") (LamAbs 2 (LamApp (LamApp
(LamVar 1) (LamVar 2)) (LamVar 0)))))))
Answer:(penalty regime: 0 %)
This is the Starting code
-- Your imports here

-- DO NOT MODIFY THESE DATATYPES


data LamMacroExpr = LamDef [ (String,LamExpr) ] LamExpr deriving
(Eq,Show,Read)
data LamExpr = LamMacro String | LamApp LamExpr LamExpr | LamAbs Int
LamExpr | LamVar Int deriving (Eq,Show,Read)

cpsTransform :: LamMacroExpr -> LamMacroExpr


cpsTransform = undefined

solveTBB :: Int -> [(EdgePoint,EdgePoint)] -> [Atom]


solveTBB = undefined

TASK 4:
For this challenge you will define functions to perform reduction of a lambda
expressions with macros for both innermost and outermost reduction strategies.
We will only use leftmost strategies throughout this challenge. A good starting
point is to remind yourself of the definitions of innermost and outermost
evaluation in Lecture 25 - Evaluation Strategies.
We say that a lambda expression with macros has terminated if it is an
expression without any redexes. The famous Church-Rosser theorem (for
lambda calculus) states that for different evaluation strategies any two such
terminated values will be the same, or at least alpha equivalent, lambda
expressions so they may differ only in how long a reduction sequence takes to
reach a terminated value. Note however that lambda reduction may in fact not
terminate. We are going to compare the differences between the lengths of
reduction sequences to terminated values for innermost and outermost
reduction for a given lambda expression with macros. We are also going to
compare the same lengths for the same expression when it is converted to
continuation-passing style. You may assume that there are no undefined macro
usages within the given lambda expressions but you should not assume that the
lambda expressions are closed.
Define functions:
innerRedn1 :: LamMacroExpr -> Maybe LamMacroExpr

outerRedn1 :: LamMacroExpr -> Maybe LamMacroExpr


that take a lambda expression with macros and perform a single reduction on
that expression, if possible, by returning the reduced expression. The functions
should implement the innermost and outermost reduction strategies
respectively.
Define a function
compareInnerOuter :: LamMacroExpr -> Int -> ( Maybe Int, Maybe Int, Maybe
Int, Maybe Int )
that takes a lambda expression with macros and a positive integer bound and
returns a 4-tuple containing the length of different reduction sequences up to a
given maximum length. For each reduction strategy the number returned should
be the number of steps needed for the expression to terminate. If the
expression does not terminate within the given bound (i.e. the number of
reduction steps is strictly greater than the bound) then a Nothing value should
be returned. Given an input expression E, the 4-tuple should contain lengths for:

1. Innermost reduction on E
2. Outermost reduction on E
3. Innermost reduction on the CPS translation of E applied to the identity:
[[ E ]] (λx0 → x0)
4. Outermost reduction on the CPS translation of E applied to the identity:
[[ E ]] (λx0 → x0)

in that order. We abuse notation here to mean


def X = [[ E1 ]] in ( [[ E2 ]] (λx0 → x0))
whenever we write
[[ def X = [[ E1 ]] in [[ E2 ]] (λx0 → x0).
For example:

Test Result

compareInnerOuter (LamDef [] (LamAbs 1 (Just 0,Just 0,Just 6,Just 6)


(LamApp (LamVar 1) (LamVar 2)))) 10

compareInnerOuter (LamDef [ ("F",LamAbs 1 (Just 1,Just 1,Just 3,Just 3)


(LamVar 1)) ] (LamMacro "F")) 10
Test Result

compareInnerOuter (LamDef [] ( LamApp (Just 1,Just 1,Just 8,Just 8)


(LamAbs 1 (LamVar 1)) (LamAbs 2 (LamVar
2)))) 10

compareInnerOuter (LamDef [] (LamApp (LamAbs (Nothing,Nothing,Nothing,Nothi


1 (LamApp (LamVar 1) (LamVar 1))) (LamAbs 1 ng)
(LamApp (LamVar 1) (LamVar 1))))) 100

compareInnerOuter (LamDef [ ("ID",LamAbs 1 (Just 4,Just 4,Just 22,Just


(LamVar 1)) , ("FST",LamAbs 1 (LamAbs 2 22)
(LamVar 1))) ] ( LamApp (LamApp (LamMacro
"FST") (LamVar 3)) (LamApp (LamMacro "ID")
(LamVar 4)))) 30

compareInnerOuter (LamDef [ ("FST", LamAbs 1 (Just 4,Just 3,Just 21,Just


(LamAbs 2 (LamVar 1)) ) ] ( LamApp (LamApp 21)
(LamMacro "FST") (LamVar 3)) (LamApp (LamAbs
1 (LamVar 1)) (LamVar 4)))) 30

compareInnerOuter (LamDef [ ("ID",LamAbs 1 (Nothing,Just


(LamVar 1)) , ("SND",LamAbs 1 (LamAbs 2 4,Nothing,Nothing)
(LamVar 2))) ] (LamApp (LamApp (LamMacro
"SND") (LamApp (LamAbs 1 (LamApp (LamVar 1)
(LamVar 1))) (LamAbs 1 (LamApp (LamVar 1)
(LamVar 1)))) ) (LamMacro "ID") ) ) 1000

Answer:(penalty regime: 0 %)
This is the Starting code
-- Your imports here

-- DO NOT MODIFY THESE DATA TYPES


data LamMacroExpr = LamDef [ (String,LamExpr) ] LamExpr deriving
(Eq,Show,Read)
data LamExpr = LamMacro String | LamApp LamExpr LamExpr | LamAbs Int
LamExpr | LamVar Int deriving (Eq,Show,Read)

innerRedn1 :: LamMacroExpr -> Maybe LamMacroExpr


innerRedn1 = undefined

outerRedn1 :: LamMacroExpr -> Maybe LamMacroExpr


outerRedn1 = undefined
compareInnerOuter :: LamMacroExpr -> Int -> (Maybe Int,Maybe Int,Maybe
Int,Maybe Int)
compareInnerOuter = undefined

Report Requirements and Rubrics:


In addition to your solutions to these programming challenges, you are asked to
submit an additional plain-text file Tests.hs with your own tests for the
challenges, and a report Report.pdf in pdf format.
You are expected to test your code carefully before submitting it and we ask
that you write a report on your development strategy. Your report should
include an explanation of how you implemented and tested your solutions. Your
report should be up to 1 page (approx 400 words). Note that this report is not
expected to explain how your code works, as this should be evident from your
commented code itself. Instead you should

 describe the development and testing tools used


 describe any programming techniques you used
 comment on their effectiveness of these

Your report should include a second page with a bibliography listing the
source(s) for any fragments of code written by other people that you have
adapted or included directly in your submission.
There are up to 10 marks out of 40 available for this part of the assessment.
The marks will be awarded as follows:
Upto 5 marks for coding style and readability
Up to 5 marks for the approach to development and testing as described in the
report and Tests.hs file.
The tables below give guidelines for marking.
Grade Coding Style Development and Testing Strategy

You have clearly


mastered this
programming
language, libraries
Proficient use of a range of development & testing tools and
Excellent and paradigm; techniques correctly and effectively to design, build and test your
5/5 your code is very
software. Excellent testing coverage.
easy to read and
understand;
excellent coding
style
Very good use of
the language and
libraries; code is
easy to Very good use of a number of development & testing tools and
Very
understand with techniques to design, build and test your software. Very good
Good 4/5
very good testing coverage.
programming
style, with only
minor flaws
Good use of the
language and
libraries; code is
mostly easy to
understand with Good use of development & testing tools and techniques to design,
Good 3/5
good build and test your software. Adequate testing coverage.
programming
style, some
improvements
possible
Acceptable use of
the language and
Adequate use of development & testing tools and techniques but
Acceptab libraries; not showing full professional competence. Testing coverage is
le 2 / 5 programming style
limited.
and readability are
borderline
Poor use of the
language and
libraries; coding
Some use of development & testing tools and techniques but
Poor 1 / 5 style and lacking professional competence. Testing coverage very limited.
readability need
significant
improvement
Language and
libraries have not
been used
Inadequa Inadequate use of development tools and techniques; far from
properly; expected
te 0 / 5 professional competence. No testing offered.
coding style is not
used; code is
difficult to read
The table below gives some guidance on coding style and readability

You should include a comment in a standard format at the start of your


code identifying you as the author, and stating that this is copyright of the
Authorship University of Southampton. Where you include any fragments from another
source, for example an on-line tutorial, you should identify where each of
these starts and ends using a similar style of comment.

Self-documenting code is preferred. If any of your code is not self-


documenting you should include an appropriate comment. Comments
Comments should be clear, concise and easy to understand and follow a common
commenting convention. They should add to rather than repeat what is
already clear from reading your code.
Variable and Names in your code should be carefully chosen to be clear and concise.
Function Consider adopting the naming conventions given in professional
Names programming guidelines and adhering to these.
Ease of It should be easy to read your program from top to bottom. This should be
Understandi organised so that there is a logical sequence of functions. Declarations
ng and should be placed where it is clear where and why they are needed. Local
Readability definitions using let and where improve comprehensibility.
Functions should be coherent and clear. If it is possible to improve the re-
usability of your code by breaking a long block of code into smaller pieces,
Logical
you should do so. On the other hand, if your code consists of blocks which
clarity
are too small, you may be able to improve its clarity by combining some of
these.
Ensure that your code can easily be maintained. Adopt a standard
convention for the layout and format of your code so that it is clear where
each statement and block begins and ends, and likewise each comment.
Maintainabili
Where the programming language provides a standard way to implement
ty
some feature, adopt this rather than a non-standard technique which is
likely to be misunderstood and more difficult to maintain. Avoid “magic
numbers” by using named constants for these instead.

You might also like