@@ -826,47 +826,55 @@ the underlying type of that source.
826
826
The example below defines two protocols for use with dice-based board games:
827
827
828
828
``` swift
829
- protocol DiceGame {
830
- var dice: Dice { get }
831
- func play ()
829
+ class DiceGame {
830
+ let sides: Int
831
+ let generator = LinearCongruentialGenerator ()
832
+ weak var delegate: Delegate?
833
+
834
+ init (sides : Int ) {
835
+ self .sides = sides
836
+ }
837
+
838
+ func roll () -> Int {
839
+ return Int (generator.random () * Double (sides)) + 1
840
+ }
841
+
842
+ func play (rounds : Int ) {
843
+ delegate? .gameDidStart (self )
844
+ for round in 1 ... rounds {
845
+ let player1 = roll ()
846
+ let player2 = roll ()
847
+ if player1 == player2 {
848
+ delegate? .game (self , didEndRound : round, winner : nil )
849
+ } else if player1 > player2 {
850
+ delegate? .game (self , didEndRound : round, winner : 1 )
851
+ } else {
852
+ delegate? .game (self , didEndRound : round, winner : 2 )
853
+ }
854
+ }
855
+ delegate? .gameDidEnd (self )
856
+ }
832
857
833
858
protocol Delegate : AnyObject {
834
859
func gameDidStart (_ game : DiceGame)
835
- func game (_ game : DiceGame, didStartNewTurnWithDiceRoll diceRoll : Int )
860
+ func game (_ game : DiceGame, didEndRound round : Int , winner : Int ? )
836
861
func gameDidEnd (_ game : DiceGame)
837
862
}
838
863
}
839
864
```
840
865
841
- <!--
842
- - test: `protocols`
843
-
844
- ```swifttest
845
- -> protocol DiceGame {
846
- var dice: Dice { get }
847
- func play()
848
- }
849
- -> protocol DiceGameDelegate: AnyObject {
850
- func gameDidStart(_ game: DiceGame)
851
- func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
852
- func gameDidEnd(_ game: DiceGame)
853
- }
854
- ```
855
- -->
856
-
857
- The ` DiceGame ` protocol is a protocol that can be adopted
858
- by any game that involves dice.
859
-
866
+ The ` DiceGame ` class implements a simple game using dice.
860
867
The ` DiceGame.Delegate ` protocol can be adopted
861
- to track the progress of a type that implements ` DiceGame ` .
868
+ to track the progress of this game .
862
869
Because the ` DiceGame.Delegate ` protocol
863
870
is always used in the context of a dice game,
864
871
it's nested inside of the ` DiceGame ` protocol.
865
- Protocols can be nested inside of other protocols
866
- and inside of type declarations like structures and classes,
872
+ Protocols can be nested
873
+ inside of type declarations like structures and classes,
867
874
as long as the outer declaration isn't generic.
868
875
For information about nesting types, see < doc:NestedTypes > .
869
876
877
+ <!-- XXX delete forward reference? -->
870
878
To prevent strong reference cycles,
871
879
delegates are declared as weak references.
872
880
For information about weak references,
@@ -878,84 +886,7 @@ A class-only protocol
878
886
is marked by its inheritance from ` AnyObject ` ,
879
887
as discussed in < doc:Protocols#Class-Only-Protocols > .
880
888
881
- Here's a version of the * Snakes and Ladders* game originally introduced in < doc:ControlFlow > .
882
- This version is adapted to use a ` Dice ` instance for its dice-rolls;
883
- to adopt the ` DiceGame ` protocol;
884
- and to notify a ` DiceGame.Delegate ` about its progress:
885
-
886
- ``` swift
887
- class SnakesAndLadders : DiceGame {
888
- let finalSquare = 25
889
- let dice = Dice (sides : 6 , generator : LinearCongruentialGenerator ())
890
- var square = 0
891
- var board: [Int ]
892
- init () {
893
- board = Array (repeating : 0 , count : finalSquare + 1 )
894
- board[03 ] = + 08 ; board[06 ] = + 11 ; board[09 ] = + 09 ; board[10 ] = + 02
895
- board[14 ] = -10 ; board[19 ] = -11 ; board[22 ] = -02 ; board[24 ] = -08
896
- }
897
- weak var delegate: DiceGame.Delegate?
898
- func play () {
899
- square = 0
900
- delegate? .gameDidStart (self )
901
- gameLoop: while square != finalSquare {
902
- let diceRoll = dice.roll ()
903
- delegate? .game (self , didStartNewTurnWithDiceRoll : diceRoll)
904
- switch square + diceRoll {
905
- case finalSquare:
906
- break gameLoop
907
- case let newSquare where newSquare > finalSquare:
908
- continue gameLoop
909
- default :
910
- square += diceRoll
911
- square += board[square]
912
- }
913
- }
914
- delegate? .gameDidEnd (self )
915
- }
916
- }
917
- ```
918
-
919
- <!--
920
- - test: `protocols`
921
-
922
- ```swifttest
923
- -> class SnakesAndLadders: DiceGame {
924
- let finalSquare = 25
925
- let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
926
- var square = 0
927
- var board: [Int]
928
- init() {
929
- board = Array(repeating: 0, count: finalSquare + 1)
930
- board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
931
- board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
932
- }
933
- weak var delegate: DiceGame.Delegate?
934
- func play() {
935
- square = 0
936
- delegate?.gameDidStart(self)
937
- gameLoop: while square != finalSquare {
938
- let diceRoll = dice.roll()
939
- delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
940
- switch square + diceRoll {
941
- case finalSquare:
942
- break gameLoop
943
- case let newSquare where newSquare > finalSquare:
944
- continue gameLoop
945
- default:
946
- square += diceRoll
947
- square += board[square]
948
- }
949
- }
950
- delegate?.gameDidEnd(self)
951
- }
952
- }
953
- ```
954
- -->
955
-
956
- For a description of the * Snakes and Ladders* gameplay,
957
- see < doc:ControlFlow#Break > .
958
-
889
+ <!-- XXX rewrite paragraph -->
959
890
This version of the game is wrapped up as a class called ` SnakesAndLadders ` ,
960
891
which adopts the ` DiceGame ` protocol.
961
892
It provides a gettable ` dice ` property and a ` play() ` method
@@ -964,11 +895,13 @@ in order to conform to the protocol.
964
895
because it doesn't need to change after initialization,
965
896
and the protocol only requires that it must be gettable.)
966
897
898
+ <!-- XXX rewrite paragraph -->
967
899
The * Snakes and Ladders* game board setup takes place within
968
900
the class's ` init() ` initializer.
969
901
All game logic is moved into the protocol's ` play ` method,
970
902
which uses the protocol's required ` dice ` property to provide its dice roll values.
971
903
904
+ <!-- XXX rewrite paragraph -->
972
905
Note that the ` delegate ` property is defined as an * optional* ` DiceGame.Delegate ` ,
973
906
because a delegate isn't required in order to play the game.
974
907
Because it's of an optional type,
@@ -988,7 +921,7 @@ If the `delegate` property is nil,
988
921
these delegate calls fail gracefully and without error.
989
922
If the ` delegate ` property is non-nil,
990
923
the delegate methods are called,
991
- and are passed the ` SnakesAndLadders ` instance as a parameter.
924
+ and are passed the ` DiceGame ` instance as a parameter.
992
925
993
926
<!--
994
927
TODO: add a cross-reference to optional chaining here.
@@ -999,54 +932,44 @@ which adopts the `DiceGame.Delegate` protocol:
999
932
1000
933
``` swift
1001
934
class DiceGameTracker : DiceGame .Delegate {
1002
- var numberOfTurns = 0
935
+ var playerScore1 = 0
936
+ var playerScore2 = 0
1003
937
func gameDidStart (_ game : DiceGame) {
1004
- numberOfTurns = 0
1005
- if game is SnakesAndLadders {
1006
- print (" Started a new game of Snakes and Ladders" )
1007
- }
1008
- print (" The game is using a \( game.dice .sides ) -sided dice" )
938
+ print (" Started a new game" )
939
+ playerScore1 = 0
940
+ playerScore2 = 0
1009
941
}
1010
- func game (_ game : DiceGame, didStartNewTurnWithDiceRoll diceRoll : Int ) {
1011
- numberOfTurns += 1
1012
- print (" Rolled a \( diceRoll ) " )
942
+ func game (_ game : DiceGame, didEndRound round : Int , winner : Int ? ) {
943
+ switch winner {
944
+ case 1 :
945
+ playerScore1 += 1
946
+ print (" Player 1 won round \( round ) " )
947
+ case 2 : playerScore2 += 1
948
+ print (" Player 2 won round \( round ) " )
949
+ default :
950
+ print (" Round was a draw" )
951
+ }
1013
952
}
1014
953
func gameDidEnd (_ game : DiceGame) {
1015
- print (" The game lasted for \( numberOfTurns ) turns" )
954
+ if playerScore1 == playerScore2 {
955
+ print (" The game ended in a draw." )
956
+ } else if playerScore1 > playerScore2 {
957
+ print (" Player 1 won!" )
958
+ } else {
959
+ print (" Player 2 won!" )
960
+ }
1016
961
}
1017
962
}
1018
963
```
1019
964
1020
- <!--
1021
- - test: `protocols`
1022
-
1023
- ```swifttest
1024
- -> class DiceGameTracker: DiceGame.Delegate {
1025
- var numberOfTurns = 0
1026
- func gameDidStart(_ game: DiceGame) {
1027
- numberOfTurns = 0
1028
- if game is SnakesAndLadders {
1029
- print("Started a new game of Snakes and Ladders")
1030
- }
1031
- print("The game is using a \(game.dice.sides)-sided dice")
1032
- }
1033
- func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
1034
- numberOfTurns += 1
1035
- print("Rolled a \(diceRoll)")
1036
- }
1037
- func gameDidEnd(_ game: DiceGame) {
1038
- print("The game lasted for \(numberOfTurns) turns")
1039
- }
1040
- }
1041
- ```
1042
- -->
1043
-
965
+ <!-- XXX rewrite paragraph -->
1044
966
` DiceGameTracker ` implements all three methods required by ` DiceGame.Delegate ` .
1045
967
It uses these methods to keep track of the number of turns a game has taken.
1046
968
It resets a ` numberOfTurns ` property to zero when the game starts,
1047
969
increments it each time a new turn begins,
1048
970
and prints out the total number of turns once the game has ended.
1049
971
972
+ <!-- XXX rewrite paragraph -->
1050
973
The implementation of ` gameDidStart(_:) ` shown above uses the ` game ` parameter
1051
974
to print some introductory information about the game that's about to be played.
1052
975
The ` game ` parameter has a type of ` DiceGame ` , not ` SnakesAndLadders ` ,
@@ -1058,6 +981,7 @@ In this example, it checks whether `game` is actually
1058
981
an instance of ` SnakesAndLadders ` behind the scenes,
1059
982
and prints an appropriate message if so.
1060
983
984
+ <!-- XXX rewrite paragraph -->
1061
985
The ` gameDidStart(_:) ` method also accesses the ` dice ` property of the passed ` game ` parameter.
1062
986
Because ` game ` is known to conform to the ` DiceGame ` protocol,
1063
987
it's guaranteed to have a ` dice ` property,
@@ -1068,36 +992,16 @@ Here's how `DiceGameTracker` looks in action:
1068
992
1069
993
``` swift
1070
994
let tracker = DiceGameTracker ()
1071
- let game = SnakesAndLadders ( )
995
+ let game = DiceGame ( sides : 6 )
1072
996
game.delegate = tracker
1073
- game.play ()
1074
- // Started a new game of Snakes and Ladders
1075
- // The game is using a 6-sided dice
1076
- // Rolled a 3
1077
- // Rolled a 5
1078
- // Rolled a 4
1079
- // Rolled a 5
1080
- // The game lasted for 4 turns
997
+ game.play (rounds : 3 )
998
+ // Started a new game
999
+ // Player 2 won round 1
1000
+ // Player 2 won round 2
1001
+ // Player 1 won round 3
1002
+ // Player 2 won!
1081
1003
```
1082
1004
1083
- <!--
1084
- - test: `protocols`
1085
-
1086
- ```swifttest
1087
- -> let tracker = DiceGameTracker()
1088
- -> let game = SnakesAndLadders()
1089
- -> game.delegate = tracker
1090
- -> game.play()
1091
- </ Started a new game of Snakes and Ladders
1092
- </ The game is using a 6-sided dice
1093
- </ Rolled a 3
1094
- </ Rolled a 5
1095
- </ Rolled a 4
1096
- </ Rolled a 5
1097
- </ The game lasted for 4 turns
1098
- ```
1099
- -->
1100
-
1101
1005
## Adding Protocol Conformance with an Extension
1102
1006
1103
1007
You can extend an existing type to adopt and conform to a new protocol,
0 commit comments