I\'m making a ro sham bo game. Functions in swift are different than what I have used before. I keep getting an error:
Variable used before being init
Try var returnval: Int = 0
(or another random number in case your if-else statements are exhaustive)
EDITTED
You're getting the initializer error because you haven't initialized returnval
with Int()
prior to setting.
Consider using a computed property to return the value of who won. I made the assumption in my code below, that the value 2 you were using inferred a tie situation based on your logic.
Here I created an enumeration to ensure the returned value is only handled in these particular ways you'd expect the outcome to mean. You can still access the Int
values via the hash value on the enum case. Use .rawValue
to do so.
It is important to try your best to avoid using Int
values in this case because they can be other values.
In the code I included below I wrote you a set of guard
statements to fail and return a fatalError
message when the values checked do not allow the winner state to be determined.
Improved Code
import Foundation
import UIKit
class Result: UIViewController {
var rval: Int?
var chosen: Int?
enum Winner: Int {
case one = 0
case two = 1
case tie = 2
}
var winner: Winner? {
guard (rval > 0 || rval < 4) else {
fatalError("rval out of bounds: cannot determine winner")
}
guard (chosen > 0 || chosen < 3) else {
fatalError("chosen out of bound: cannot determine winner")
}
guard (rval != nil && chosen != nil) else {
fatalError("rval or chosen are nil: cannot determine winner")
}
switch (chosen!, rval!) {
case (let chosen, let rval) where chosen == rval: return Winner.tie
case (1, 3): return Winner.two
case (1, 2): return Winner.one
case (2, 1): return Winner.two
default:
return nil
}
}
@IBOutlet weak var wl: UILabel!
@IBAction func PlayAgain(sender: AnyObject) {
}
override func viewDidLoad() {
print(chosen)
}
}
As a side note be sure to +1 the answers you like!
You can make this much more swifty.
For instance... Why use Int
to represent the different moves? Here I used an enum to represent the moves and the logic behind what wins...
enum RoShamBo {
case Rock
case Paper
case Scissors
func beats(opposingPlay: RoShamBo) -> Bool {
return self > opposingPlay
}
}
// I thought it made sense to make it Comparable.
// That way you can use the < or > operator to determine
// the winning move.
// I then used this in the function beats() above
extension RoShamBo: Comparable {}
func < (lhs: RoShamBo, rhs: RoShamBo) -> Bool {
// this is required for Comparable
// in this we return true if lhs loses to rhs
// Scissors beat Paper
return lhs == .Paper && rhs == .Scissors
// Paper beats Rock
|| lhs == .Rock && rhs == .Paper
// Rock beats Scissors
|| lhs == .Scissors && rhs == .Rock
}
Then all you need to do is wrap this up in some sort of Player
and Game
type thing...
struct Player {
let name: String
let move: RoShamBo
}
struct Game {
func winner(player1: Player, player2: Player) -> Player? {
if player1.move.beats(opposingPlay: player2.move) {
return player1
}
if player2.move.beats(opposingPlay: player1.move) {
return player2
}
// tie
return nil
}
}
let p1 = Player(name: "Oliver", move: .Rock)
let p2 = Player(name: "Geoff", move: .Scissors)
let game = Game()
let winner = game.winner(player1: p1, player2: p2)
print(winner)
//Output Optional(Player(name: "Oliver", move: RoShamBo.Rock))
Not a single Int was used in the whole thing and you can see exactly what the winning move was and who won etc...
Enums are really a lot more powerful than they are given credit for in Swift.
The problematic statement is return (returnval)
because Swift compiler thinks that there is a pass through the if-then-else chain that does not result in assignment of returnval
.
For example, if chosen
is 3 and rval
is 2, there would be no assignment.
Perhaps other parts of your program makes it impossible for chosen
to be 3 at the same time as rval
is 2, but Swift has no idea of that, so it reports an error. In order to fix the compile error, add an initial value to returnval
.
If you are absolutely sure that your if-then-else chain enumerates all valid possibilities, set returnval
to -1
, and make an assertion about it being set to a non-negative value before returning:
var returnval = -1
... // your conditionals go here
assert(returnval >= 0, "Logic of determineWinner is broken")
return returnval
Every possible path in the flow of a Swift program must have a defined returned value. In your case, if the if
/else if
/else
sections are all skipped, leaving returnval
unassigned. Thus, no valid value is being returned. Try this:
import Foundation
import UIKit //space things out to be easier to read
class Result: UIViewController {
var rval: Int? //these should have better names
var chosen: Int?
func determineWinner() -> Int {
var returnval = -1 //needs a default value
if (chosen == rval){
returnval = 2
}
else if (chosen == 1 && rval == 3){
returnval = 1
}
else if (chosen == 1 && rval == 2){
returnval = 0
}
else if (chosen == 2 && rval == 1){
returnval = 1
}
return returnval //don't put return value in brackets
}
@IBOutlet weak var wl: UILabel!
@IBAction func PlayAgain(sender: AnyObject) {
}
override func viewDidLoad() {
print(chosen)
}
}
This is also a very good candidate for using pattern matching with switch statements. Here's what I think works beside, in conjunction withSean's suggestion.
var determineWinner: Int? {
guard let chosen = chosen, let rval = rval else {
//handle error, either chosen or rval is nil
return nil
}
switch ((chosen, rval)) {
case let (x, y) where x == y: return 2
case (1, 3): return 1
case (1, 2): return 0
case (2, 1): return 1
default:
print("handle default case here")
return nil;
}
}
As others already said, the problem is that none of your conditions is met then returnval
is not initialized.
You can use a switch statement + guard + computed property. Like this
var winner: Int? {
guard let chosen = chosen, rval = rval else { return nil }
switch (chosen, rval) {
case (let chosen, let rval) where chosen == rval : return 2
case (1, 3): return 1
case (1, 2): return 0
case (2, 1): return 1
default: return nil
}
}
Please note I slightly changed your logic. Infact in my code if chosen and rval are both nil the returned value is nil. While in your code 2 is returned. You should change it, maybe adding another
guard
on top of my code. Something like
guard chosen != rval else { return 2 }
If you know chose and rval will always be populated then
var winner: Int {
guard let chosen = chosen, rval = rval else { fatalError() }
switch (chosen, rval) {
case (let chosen, let rval) where chosen == rval : return 2
case (1, 3): return 1
case (1, 2): return 0
case (2, 1): return 1
default: fatalError()
}
}