Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rectangles solution #373

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions dev/src/Exercise@Rectangles/Array2D.extension.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Extension { #name : #Array2D }

{ #category : #'*Exercise@Rectangles' }
Array2D >> isLineAt: aLineSegment [
"Answer true, if a LineSegment identifies a valid line in my space.
Lines can be composed of +,-,| characters, anything else is invalid"

| lineChars |

lineChars := aLineSegment points
collect: [ :point | self at: point x at: point y ]
thenReject: [ :char | char = $+ ].

^ (lineChars
reject: [ :char |
char =
(aLineSegment isRow
ifTrue: [ $- ]
ifFalse: [ $| ]) ]) ifEmpty: [ true ] ifNotEmpty: [ false ]
]
17 changes: 17 additions & 0 deletions dev/src/Exercise@Rectangles/LineSegment.extension.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Extension { #name : #LineSegment }

{ #category : #'*Exercise@Rectangles' }
LineSegment >> isRow [
"Answer true if the segment is on the same row (the internal point x@y is storing row@col)"

^self start x = self end x
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't look like a boolean result (??) Typo 'minus' instead of 'equals' ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a boolean result - are the x values the same - however it did prompt me to change the name as horizontal ness is confusing if x@y are cartesian, where im actually using the point to represent row@column (so a bit of a stretch). I figure if the method is, isRow (and I used a comment) that makes it a bit better)

]

{ #category : #'*Exercise@Rectangles' }
LineSegment >> points [
"Answer the points either along the same row, or the same column"

^ self isRow
ifTrue: [ (self start y to: self end y) collect: [ :y | self start x @ y ] ]
ifFalse: [ (self start x to: self end x) collect: [ :x | x @ self start y ] ]
]
10 changes: 10 additions & 0 deletions dev/src/Exercise@Rectangles/Point.extension.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Extension { #name : #Point }

{ #category : #'*Exercise@Rectangles' }
Point >> region: aPoint [
"Answer a Region that encompasses the receiver and aPoint."

^ Region
topLeft: self
bottomRight: aPoint
]
69 changes: 69 additions & 0 deletions dev/src/Exercise@Rectangles/Rectangles.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"
Mentor notes:

- this solution identifies potential rectangles, by searching for + corners in a matrix, deriving valid rectangle permuations from them, and then checking if those derived rectangles actually have lines joining the corners
- this solution had to use a new Region object, as Rectangles optimise their coordinates
"
Class {
#name : #Rectangles,
#superclass : #Object,
#category : #'Exercise@Rectangles'
}

{ #category : #helper }
Rectangles >> getAllRectangleCornersIn: matrix [
^ matrix
withIndicesInject: OrderedCollection new
into: [ :result :value :r :c |
value = $+
ifTrue: [ result add: r @ c ].
result ]
]

{ #category : #helper }
Rectangles >> possibleRectangleRegionsIn: matrix [
"Answer all possible Regions in the matrix, where there could be a rectangle"

| corners |
corners := self getAllRectangleCornersIn: matrix.

^ corners
inject: Set new
into: [ :result :corner |
| lowerCorners |

lowerCorners := self potentialLowerCornersFor: corner given: corners.

result
addAll:
(lowerCorners
collect: [ :lc | corner region: lc ]
thenSelect: [ :region | region isValid ]);
yourself ]
]

{ #category : #helper }
Rectangles >> potentialLowerCornersFor: topLeftCorner given: corners [
^ corners
reject: [ :corner |
corner = topLeftCorner | (corner x < topLeftCorner x)
| (corner y < topLeftCorner y) ]
]

{ #category : #exercism }
Rectangles >> rectanglesFromStrings: aCollection [
"Answer the number of rectangles recognized in aCollection of Strings"

| matrix |

aCollection ifEmpty: [ ^ 0 ].

matrix := Array2D
rows: aCollection size
columns: aCollection first size
contents: (aCollection flatCollect: [ :c | c ]).

^(self possibleRectangleRegionsIn: matrix) count: [ :region |
region definesRectangleIn: matrix ]

]
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ every line equals the length of the first line).

## Hint

TBD
You may be able to use a brute force strategy to solve this one.

"
Class {
Expand All @@ -75,9 +75,29 @@ Class {
#instVars : [
'rectanglesCalculator'
],
#category : #'ExercismWIP-Rectangles'
#category : #'Exercise@Rectangles'
}

{ #category : #config }
RectanglesTest class >> exercise [
"Answer the configured exercise meta data for this exercise, an ExercismExercise"

^(self createExerciseAfter: MatrixTest)
isCore: false;
difficulty: 7;
topics: #('transforming' 'parsing' 'pattern_recognition');
yourself


]

{ #category : #generator }
RectanglesTest class >> generator [
"Answer code generator aliases/hints"

^{'@rectangleStrings' -> #rectanglesFromStrings}
]

{ #category : #config }
RectanglesTest class >> uuid [
"Answer a unique id for this exercise"
Expand All @@ -100,102 +120,147 @@ RectanglesTest >> setUp [
RectanglesTest >> test01_NoRows [
| result |

result := rectanglesCalculator rectanglesStrings: #() .
result := rectanglesCalculator rectanglesFromStrings: #() .
self assert: result equals: 0
]

{ #category : #tests }
RectanglesTest >> test02_NoColumns [
| result |

result := rectanglesCalculator rectanglesStrings: #('' ) .
result := rectanglesCalculator rectanglesFromStrings: #('' ) .
self assert: result equals: 0
]

{ #category : #tests }
RectanglesTest >> test03_NoRectangles [
| result |

result := rectanglesCalculator rectanglesStrings: #(' ' ) .
result := rectanglesCalculator rectanglesFromStrings: #(' ' ) .
self assert: result equals: 0
]

{ #category : #tests }
RectanglesTest >> test04_OneRectangle [
| result |

result := rectanglesCalculator rectanglesStrings: #('+-+' '| |' '+-+' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
'+-+'
'| |'
'+-+' ) .

self assert: result equals: 1
]

{ #category : #tests }
RectanglesTest >> test05_TwoRectanglesWithoutSharedParts [
| result |

result := rectanglesCalculator rectanglesStrings: #(' +-+' ' | |' '+-+-+' '| | ' '+-+ ' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
' +-+'
' | |'
'+-+-+'
'| | '
'+-+ ' ) .

self assert: result equals: 2
]

{ #category : #tests }
RectanglesTest >> test06_FiveRectanglesWithSharedParts [
| result |

result := rectanglesCalculator rectanglesStrings: #(' +-+' ' | |' '+-+-+' '| | |' '+-+-+' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
' +-+'
' | |'
'+-+-+'
'| | |'
'+-+-+' ) .
self assert: result equals: 5
]

{ #category : #tests }
RectanglesTest >> test07_RectangleOfHeight1IsCounted [
| result |

result := rectanglesCalculator rectanglesStrings: #('+--+' '+--+' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
'+--+'
'+--+' ) .

self assert: result equals: 1
]

{ #category : #tests }
RectanglesTest >> test08_RectangleOfWidth1IsCounted [
| result |

result := rectanglesCalculator rectanglesStrings: #('++' '||' '++' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
'++'
'||'
'++' ) .

self assert: result equals: 1
]

{ #category : #tests }
RectanglesTest >> test09_1x1SquareIsCounted [
| result |

result := rectanglesCalculator rectanglesStrings: #('++' '++' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
'++'
'++' ) .

self assert: result equals: 1
]

{ #category : #tests }
RectanglesTest >> test10_OnlyCompleteRectanglesAreCounted [
| result |

result := rectanglesCalculator rectanglesStrings: #(' +-+' ' |' '+-+-+' '| | -' '+-+-+' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
' +-+'
' |'
'+-+-+'
'| | -'
'+-+-+' ) .

self assert: result equals: 1
]

{ #category : #tests }
RectanglesTest >> test11_RectanglesCanBeOfDifferentSizes [
| result |

result := rectanglesCalculator rectanglesStrings: #('+------+----+' '| | |' '+---+--+ |' '| | |' '+---+-------+' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
'+------+----+'
'| | |'
'+---+--+ |'
'| | |'
'+---+-------+' ) .
self assert: result equals: 3
]

{ #category : #tests }
RectanglesTest >> test12_CornerIsRequiredForARectangleToBeComplete [
| result |

result := rectanglesCalculator rectanglesStrings: #('+------+----+' '| | |' '+------+ |' '| | |' '+---+-------+' ) .
result := rectanglesCalculator rectanglesFromStrings: #('+------+----+' '| | |' '+------+ |' '| | |' '+---+-------+' ) .
self assert: result equals: 2
]

{ #category : #tests }
RectanglesTest >> test13_LargeInputWithManyRectangles [
| result |

result := rectanglesCalculator rectanglesStrings: #('+---+--+----+' '| +--+----+' '+---+--+ |' '| +--+----+' '+---+--+--+-+' '+---+--+--+-+' '+------+ | |' ' +-+' ) .
result := rectanglesCalculator rectanglesFromStrings: #(
'+---+--+----+'
'| +--+----+'
'+---+--+ |'
'| +--+----+'
'+---+--+--+-+'
'+---+--+--+-+'
'+------+ | |'
' +-+' ) .

self assert: result equals: 60
]
Loading