Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
/*:
## Exercise - Define a Base Class

- Note: The exercises below are based on a game where a spaceship avoids obstacles in space. The ship is positioned at the bottom of a coordinate system and can only move left and right while obstacles "fall" from top to bottom. Throughout the exercises, you'll create classes to represent different types of spaceships that can be used in the game.

Create a `Spaceship` class with three variable properties: `name`, `health`, and `position`. The default value of `name` should be an empty string and `health` should be 0. `position` will be represented by an `Int` where negative numbers place the ship further to the left and positive numbers place the ship further to the right. The default value of `position` should be 0.
*/


/*:
Create a `let` constant called `falcon` and assign it to an instance of `Spaceship`. After initialization, set `name` to "Falcon".
*/
## Exercise - Define a Base Class

- Note: The exercises below are based on a game where a spaceship avoids obstacles in space. The ship is positioned at the bottom of a coordinate system and can only move left and right while obstacles "fall" from top to bottom. Throughout the exercises, you'll create classes to represent different types of spaceships that can be used in the game.

/*:
Go back and add a method called `moveLeft()` to the definition of `Spaceship`. This method should adjust the position of the spaceship to the left by one. Add a similar method called `moveRight()` that moves the spaceship to the right. Once these methods exist, use them to move `falcon` to the left twice and to the right once. Print the new position of `falcon` after each change in position.
Create a `Spaceship` class with three variable properties: `name`, `health`, and `position`. The default value of `name` should be an empty string and `health` should be 0. `position` will be represented by an `Int` where negative numbers place the ship further to the left and positive numbers place the ship further to the right. The default value of `position` should be 0.
*/

class Spaceship {
var name = ""
var health = 0
var position = 0

func moveLeft() {
position -= 1
}

func moveRight() {
position += 1
}

func wasHit() {
health -= 5

if health <= 0 {
print("Sorry, your ship was hit one too many times. Do you want to play again?")
}
}
}

//: Create a `let` constant called `falcon` and assign it to an instance of `Spaceship`. After initialization, set `name` to "Falcon".
let falcon = Spaceship()
falcon.name = "Falcon"

//: Go back and add a method called `moveLeft()` to the definition of `Spaceship`. This method should adjust the position of the spaceship to the left by one. Add a similar method called `moveRight()` that moves the spaceship to the right. Once these methods exist, use them to move `falcon` to the left twice and to the right once. Print the new position of `falcon` after each change in position.
falcon.moveLeft()
print(falcon.position)
falcon.moveLeft()
print(falcon.position)
falcon.moveRight()
print(falcon.position)
//: The last thing `Spaceship` needs for this example is a method to handle what happens if the ship gets hit. Go back and add a method `wasHit()` to `Spaceship` that will decrement the ship's health by 5, then if `health` is less than or equal to 0 will print "Sorry, your ship was hit one too many times. Do you want to play again?" Once this method exists, call it on `falcon` and print out the value of `health`.
falcon.wasHit()
print(falcon.health)

/*:
The last thing `Spaceship` needs for this example is a method to handle what happens if the ship gets hit. Go back and add a method `wasHit()` to `Spaceship` that will decrement the ship's health by 5, then if `health` is less than or equal to 0 will print "Sorry. Your ship was hit one too many times. Do you want to play again?" Once this method exists, call it on `falcon` and print out the value of `health`.
page 1 of 4 | [Next: Exercise - Create a Subclass](@next)
*/


//: page 1 of 4 | [Next: Exercise - Create a Subclass](@next)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*:
## Exercise - Create a Subclass
## Exercise - Create a Subclass

- Note: The exercises below are based on a game where a spaceship avoids obstacles in space. The ship is positioned at the bottom of a coordinate system and can only move left and right while obstacles "fall" from top to bottom. Throughout the exercises, you'll create classes to represent different types of spaceships that can be used in the game. The base class `Spaceship` has been provided for you below.
*/
class Spaceship {
Expand All @@ -23,24 +23,37 @@ class Spaceship {
}
}
}
/*:
Define a new class `Fighter` that inherits from `Spaceship`. Add a variable property `weapon` that defaults to an empty string and a variable property `remainingFirePower` that defaults to 5.
*/


/*:
Create a new instance of `Fighter` called `destroyer`. A `Fighter` will be able to shoot incoming objects to avoid colliding with them. After initialization, set `weapon` to "Laser" and `remainingFirePower` to 10. Note that since `Fighter` inherits from `Spaceship`, it also has properties for `name`, `health`, and `position`, and has methods for `moveLeft()`, `moveRight()`, and `wasHit()` even though you did not specifically add them to the declaration of `Fighter`. Knowing that, set `name` to "Destroyer," print `position`, then call `moveRight()` and print `position` again.
*/


/*:
Try to print `weapon` on `falcon`. Why doesn't this work? Provide your answer in a comment or a print statement below, and remove any code you added that doesn't compile.
*/
//: Define a new class `Fighter` that inherits from `Spaceship`. Add a variable property `weapon` that defaults to an empty string and a variable property `remainingFirePower` that defaults to 5.
class Fighter: Spaceship {
var weapon = ""
var remainingPower = 5
func fire() {
if remainingPower > 0 {
remainingPower -= 1
} else {
print("You have no more fire power.")
}
}
}

//: Create a new instance of `Fighter` called `destroyer`. A `Fighter` will be able to shoot incoming objects to avoid colliding with them. After initialization, set `weapon` to "Laser" and `remainingFirePower` to 10. Note that since `Fighter` inherits from `Spaceship`, it also has properties for `name`, `health`, and `position`, and has methods for `moveLeft()`, `moveRight()`, and `wasHit()` even though you did not specifically add them to the declaration of `Fighter`. Knowing that, set `name` to "Destroyer," print `position`, then call `moveRight()` and print `position` again.
let destroyer = Fighter()
destroyer.weapon = "Laser"
destroyer.remainingPower = 10
destroyer.name = "Destroyer"
print(destroyer.position)
destroyer.moveRight()
print(destroyer.position)
//: Try to print `weapon` on `falcon`. Why doesn't this work? Provide your answer in a comment or a print statement below, and remove any code you added that doesn't compile.
//print(falcon.weapon)
print("falcon was an instance of class Spaceship which doesn't have a weapon property. Besides that falcon is outside the scope of this file. ;)")
//: Add a method to `fighter` called `fire()`. This should check to see if `remainingFirePower` is greater than 0, and if so, should decrement `remainingFirePower` by one. If `remainingFirePower` is not greater than 0, print "You have no more fire power." Call `fire()` on `destroyer` a few times and print `remainingFirePower` after each method call.
for _ in 1...13 {
print("Destroyer's fire power: \(destroyer.remainingPower)")
destroyer.fire()
}

/*:
Add a method to `fighter` called `fire()`. This should check to see if `remainingFirePower` is greater than 0, and if so, should decrement `remainingFirePower` by one. If `remainingFirePower` is not greater than 0, print "You have no more fire power." Call `fire()` on `destroyer` a few times and print `remainingFirePower` after each method call.
[Previous](@previous) | page 2 of 4 | [Next: Exercise - Override Methods and Properties](@next)
*/


//: [Previous](@previous) | page 2 of 4 | [Next: Exercise - Override Methods and Properties](@next)
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*:
## Exercise - Override Methods and Properties
## Exercise - Override Methods and Properties

- Note: The exercises below are based on a game where a spaceship avoids obstacles in space. The ship is positioned at the bottom of a coordinate system and can only move left and right while obstacles "fall" from top to bottom. Throughout the exercises, you'll create classes to represent different types of spaceships that can be used in the game. The base class `Spaceship` and one subclass `Fighter` have been provided for you below.
*/
*/
class Spaceship {
var name: String = ""
var health = 100
Expand Down Expand Up @@ -36,19 +36,39 @@ class Fighter: Spaceship {
}
}
}
/*:
Define a new class `ShieldedShip` that inherits from `Fighter`. Add a variable property `shieldStrength` that defaults to 25. Create a new instance of `ShieldedShip` called `defender`. Set `name` to "Defender" and `weapon` to "Cannon." Call `moveRight()` and print `position`, then call `fire()` and print `remainingFirePower`.
*/

//: Define a new class `ShieldedShip` that inherits from `Fighter`. Add a variable property `shieldStrength` that defaults to 25. Create a new instance of `ShieldedShip` called `defender`. Set `name` to "Defender" and `weapon` to "Cannon." Call `moveRight()` and print `position`, then call `fire()` and print `remainingFirePower`.
class ShieldedShip: Fighter {
var shieldStrength = 25

/*:
Go back to your declaration of `ShieldedShip` and override `wasHit()`. In the body of the method, check to see if `shieldStrength` is greater than 0. If it is, decrement `shieldStrength` by 5. Otherwise, decrement `health` by 5. Call `wasHit()` on `defender` and print `shieldStrength` and `health`.
*/
override func wasHit() {
if shieldStrength > 0 {
shieldStrength -= 5
} else {
super.wasHit()
}
}
}

let defender = ShieldedShip()
defender.name = "Defender"
defender.weapon = "Cannon"

/*:
When `shieldStrength` is 0, all `wasHit()` does is decrement `health` by 5. That's exactly what the implementation of `wasHit()` on `Spaceship` does! Instead of rewriting that, you can call through to the superclass implementation of `wasHit()`. Go back to your implementation of `wasHit()` on `ShieldedShip` and remove the code where you decrement `health` by 5 and replace it with a call to the superclass' implementation of the method. Call `wasHit()` on `defender`, then print `shieldStrength` and `health`.
*/
print(defender.position)
defender.moveRight()
print(defender.position)

print(defender.remainingFirePower)
defender.fire()
print(defender.remainingFirePower)
//: Go back to your declaration of `ShieldedShip` and override `wasHit()`. In the body of the method, check to see if `shieldStrength` is greater than 0. If it is, decrement `shieldStrength` by 5. Otherwise, decrement `health` by 5. Call `wasHit()` on `defender` and print `shieldStrength` and `health`.
print("defender's shield: \(defender.shieldStrength) and health: \(defender.health)")
for _ in 1...10 {
defender.wasHit()
print("defender's shield: \(defender.shieldStrength) and health: \(defender.health)")
}
//: When `shieldStrength` is 0, all `wasHit()` does is decrement `health` by 5. That's exactly what the implementation of `wasHit()` on `Spaceship` does! Instead of rewriting that, you can call through to the superclass implementation of `wasHit()`. Go back to your implementation of `wasHit()` on `ShieldedShip` and remove the code where you decrement `health` by 5 and replace it with a call to the superclass' implementation of the method. Call `wasHit()` on `defender`, then print `shieldStrength` and `health`.


//: [Previous](@previous) | page 3 of 4 | [Next: Exercise - Class Memberwise Initializers and References](@next)
/*:
[Previous](@previous) | page 3 of 4 | [Next: Exercise - Class Memberwise Initializers and References](@next)
*/
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*:
## Exercise - Class Memberwise Initializers and References
## Exercise - Class Memberwise Initializers and References

- Note: The exercises below are based on a game where a spaceship avoids obstacles in space. The ship is positioned at the bottom of a coordinate system and can only move left and right while obstacles "fall" from top to bottom. The base class `Spaceship` and subclasses `Fighter` and `ShieldedShip` have been provided for you below. You will use these to complete the exercises.
*/
Expand All @@ -8,6 +8,12 @@ class Spaceship {
var health: Int
var position: Int

init(name: String, health: Int, position: Int) {
self.name = name
self.health = health
self.position = position
}

func moveLeft() {
position -= 1
}
Expand All @@ -28,6 +34,12 @@ class Fighter: Spaceship {
let weapon: String
var remainingFirePower: Int

init(name: String, health: Int, position: Int, weapon: String, remainingPower: Int) {
self.weapon = weapon
self.remainingFirePower = remainingPower
super.init(name: name, health: health, position: position)
}

func fire() {
if remainingFirePower > 0 {
remainingFirePower -= 1
Expand All @@ -40,6 +52,11 @@ class Fighter: Spaceship {
class ShieldedShip: Fighter {
var shieldStrength: Int

init(name: String, health: Int, position: Int, weapon: String, remainingPower: Int, shieldStrength: Int) {
self.shieldStrength = shieldStrength
super.init(name: name, health: health, position: position, weapon: weapon, remainingPower: remainingPower)
}

override func wasHit() {
if shieldStrength > 0 {
shieldStrength -= 5
Expand All @@ -49,39 +66,41 @@ class ShieldedShip: Fighter {
}
}
/*:
Note that each class above has an error by the class declaration that says "Class has no initializers." Unlike structs, classes do not come with memberwise initializers because the standard memberwise initializers don't always play nicely with inheritance. You can get rid of the error by providing default values for everything, but it is common and better practice to simply write your own initializer. Go to the declaration of `Spaceship` and add an initializer that takes in an argument for each property on `Spaceship` and sets the properties accordingly.
Note that each class above has an error by the class declaration that says "Class has no initializers." Unlike structs, classes do not come with memberwise initializers because the standard memberwise initializers don't always play nicely with inheritance. You can get rid of the error by providing default values for everything, but it is common, and better practice, to simply write your own initializer. Go to the declaration of `Spaceship` and add an initializer that takes in an argument for each property on `Spaceship` and sets the properties accordingly.

Then create an instance of `Spaceship` below called `falcon`. Use the memberwise initializer you just created. The ship's name should be "Falcon."
*/

let falcon = Spaceship(name: "Falcon", health: 0, position: 0)

/*:
Writing initializers for subclasses can get tricky. Your initializer needs to not only set the properties declared on the subclass, but also set all of the uninitialized properties on classes that it inherits from. Go to the declaration of `Fighter` and write an initializer that takes an argument for each property on `Fighter` and for each property on `Spaceship`. Set the properties accordingly. (Hint: you can call through to a superclass' initializer with `super.init` *after* you initialize all of the properties on the subclass).

Then create an instance of `Fighter` below called `destroyer`. Use the memberwise initializer you just created. The ship's name should be "Destroyer."
*/

let destroyer = Fighter(name: "Destroyer", health: 100, position: 0, weapon: "Laser", remainingPower: 10)

/*:
Now go add an initializer to `ShieldedShip` that takes an argument for each property on `ShieldedShip`, `Fighter`, and `Spaceship`, and sets the properties accordingly. Remember that you can call through to the initializer on `Fighter` using `super.init`.

Then create an instance of `ShieldedShip` below called `defender`. Use the memberwise initializer you just created. The ship's name should be "Defender."
*/
let defender = ShieldedShip(name: "Defender", health: 100, position: 0, weapon: "Cannon", remainingPower: 10, shieldStrength: 25)

//: Create a new constant named `sameShip` and set it equal to `falcon`. Print out the position of `sameShip` and `falcon`, then call `moveLeft()` on `sameShip` and print out the position of `sameShip` and `falcon` again. Did both positions change? Why? If both were structs instead of classes, would it be the same? Why or why not? Provide your answer in a comment or print statement below.
let sameShip = falcon
print("falcon's position: \(falcon.position), sameShip's position: \(sameShip.position)")
sameShip.moveLeft()
print("falcon's position: \(falcon.position), sameShip's position: \(sameShip.position)")

print("With structs, each time we are creating separate copies (instances) of them. When it comes to classes we are just using memory pointers to the same instances in memory.")
/*:
Create a new constant named `sameShip` and set it equal to `falcon`. Print out the position of `sameShip` and `falcon`, then call `moveLeft()` on `sameShip` and print out the position of `sameShip` and `falcon` again. Did both positions change? Why? If both were structs instead of classes, would it be the same? Why or why not? Provide your answer in a comment or print statement below.
*/


/*:

_Copyright © 2018 Apple Inc._
_Copyright © 2021 Apple Inc._

_Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:_

_The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software._

_THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._

[Previous](@previous) | page 4 of 4
*/
//: [Previous](@previous) | page 4 of 4
2 changes: 1 addition & 1 deletion Lab - Classes.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='ios' display-mode='rendered'>
<playground version='6.0' target-platform='macos' display-mode='rendered' executeOnSourceChanges='true'>
<pages>
<page name='1. Exercise - Define a Base Class'/>
<page name='2. Exercise - Create a Subclass'/>
Expand Down
Loading