# Regions - Daniel's Intermediate Approach

``````import Darwin
struct Position { let x, y: Float }
typealias Distance = Float

// use the "key trick" from the FP approach, but in an OO manner
protocol Containsable
{
func contains(position: Position) -> Bool
}

// Make structs that adopt the protocol
struct Circle: Containsable
{
func contains(position: Position) -> Bool
{
}
}

// example
unitCircle.contains(Position(x: 0.5, y: 0.5))
unitCircle.contains(Position(x: 2, y: 3))

// We can easily do rect also
struct Rectangle: Containsable
{
let width: Distance
let height: Distance
func contains(position: Position) -> Bool {
return (fabs(position.x) <= width/2
&& fabs(position.y) <= height/2)
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Transformations

// First, define a composition (a new struct that adopts the protocol)
struct ComposedRegion: Containsable
{
let containsRule: Position -> Bool  ///< holds the complex logic as a function

func contains(position: Position) -> Bool
{
return containsRule(position)
}
}

// Now, do the transformations (that return the new type)

func shift(region: Containsable, by offset: Position) -> ComposedRegion
{
return ComposedRegion(containsRule: {
point in
let shiftedPoint = Position(x: point.x - offset.x, y: point.y - offset.y)
return region.contains(shiftedPoint)
})
}

// example
let shiftedCirlce = shift(unitCircle, by: Position(x: 1.5, y: 3.5))
shiftedCirlce.contains(Position(x: 2, y: 3))

// Other Operations
func invert(region: Containsable) -> ComposedRegion
{
return ComposedRegion(containsRule: { point in !region.contains(point) })
}

func intersection(of a: Containsable, with b: Containsable) -> ComposedRegion
{
return ComposedRegion(containsRule: { point in
a.contains(point) && b.contains(point)
})
}

func union(of a: Containsable, with b: Containsable) -> ComposedRegion
{
return ComposedRegion(containsRule: { point in
a.contains(point) || b.contains(point)
})
}

func difference(of region: Containsable, minusRegion: Containsable) -> ComposedRegion
{
return intersection(of: region, with: invert(minusRegion))
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Now, the complex example
// Note: It should look a lot like the FP solution of this

let ownPosition = Position(x: 10, y: 12)
let weaponRange = Circle(radius: 5.0)   // <-- just make this a struct
let safeDistance = Circle(radius: 1.0)   // <-- and this
let friendlyPosition = Position(x: 12, y: 9)
let friendlyRegion = shift(safeDistance, by: friendlyPosition)

let shouldFireAtTarget = difference(
of: shift(
difference(
of: weaponRange,
minusRegion: safeDistance),
by: ownPosition),
minusRegion: friendlyRegion)

// Test it
shouldFireAtTarget.contains(Position(x: 0, y: 0))  // too far away
shouldFireAtTarget.contains(Position(x: 9, y: 15))  // hit!
shouldFireAtTarget.contains(Position(x: 10.5, y: 12))  // too close to self
shouldFireAtTarget.contains(Position(x: 12.25, y: 9.25))  // too close to friendly
``````