cardigan cardigan - 6 months ago 32
Swift Question

Swift - How to manage turn order management?

I'm working on a local game which relies on turn order.

Rules;


  • There are a number of
    phases
    in the game (ie: Buy, Sell)

  • During each
    phase
    , a player takes a single turn

  • Each
    phase
    is not considered complete until every player (in turn order) has completed their turn.



I'm not sure how to manage this data. There are a number of things to track.


  1. The phase we are in

  2. The current player on turn

  3. When all players have completed their turns

  4. When the end of the turn order has been reached so we can move to next phase.

  5. Resetting all turn completions when all phases are complete



I'm thinking that a subscription model is the best approach to this, but I'm not used to such a pattern.

Currently I'm using a similar system to a to-do where the phase itself can be marked complete or incomplete.

This is the way I'm currently handling turn orders and phases in Swift playground.

// Turn order management

class TurnOrderManager: NSObject
{
static var instance = TurnOrderManager()

var turnOrder: [Player] = [Player]()
private var turnOrderIndex = 0

var current: Player {
return turnOrder[turnOrderIndex]
}

func next() {
if (self.turnOrderIndex < (self.turnOrder.count-1)) {
turnOrderIndex += 1
}
else {
print("Reached end of line")
}
}
}

class Player: NSObject {
var name: String = ""

override var description: String {
return self.name
}

init(name: String) {
super.init()
self.name = name
}
}

let p1:Player = Player.init(name: "Bob")
let p2:Player = Player.init(name: "Alex")

TurnOrderManager.instance.turnOrder = [p1,p2]
print (TurnOrderManager.instance.current)

TurnOrderManager.instance.next()
print (TurnOrderManager.instance.current)

TurnOrderManager.instance.next()
print (TurnOrderManager.instance.current)


// ---------------------------------

// Phase management
enum PhaseType: Int {
case buying = 1
case selling
}

struct Phase {
var id: PhaseType
var title: String
var completed: Bool = false {
didSet {
// Notify subscribers of completion
guard completed else { return }
handlers.forEach { $0(self) }
}
}

var description:String {
return "Phase: \(self.title), completed: \(completed)"
}

// Task queue
var handlers = [(Phase) -> Void]()

init(id: PhaseType, title: String, initialSubscription: @escaping (Phase) -> Void =
{_ in})
{
self.id = id
self.title = title
subscribe(completion: initialSubscription)
}

mutating func subscribe(completion: @escaping (Phase) -> Void) {
handlers.append(completion)
}
}

class MyParentController {

lazy var phase1: Phase = {
return Phase(id: .buying, title: "Phase #1") {
print("Do something with phase: \($0.title)")
}
}()

}

let controller = MyParentController()

controller.phase1.completed = true





Question:

I'm wanting to notify:


  • Turn is complete

  • All turns are complete (so that it can move to next phase)



How do I make my TurnOrderManager alert the PhaseManager that the current turn is complete.

How do I make my PhaseManager know that when all turns are complete to move to the next phase.

I apologize for the verboseness of my query.

Many thanks

Answer Source

You're going to want to define a delegate relationship PhaseManager and your TurnOrderManager.

Here is the Apple docs on protocols.

Here is a great article on delegation.

First you'll need to define a protocol. Something like this:

protocol TurnManagerDelegate {
   func complete(turn: objectType)
   func allTurnsComplete()
}

Next you'll have to conform your PhaseManager to the protocol:

class PhaseManager: TurnManagerDelegate {
   ...

   func complete(turn: objectType) {
      // implement 
   }

   func allTurnsComplete() {
      // implement 
   }
   ...
}

Last you'll have to add a property on your TurnOrderManager with the delegate:

class TurnOrderManager {
...
   var delegate: TurnManagerDelegate
...
}

and call the functions whenever needed in your TurnOrderManager:

...
delegate?.allTurnsComplete() //example
...

You'll also have to set your PhaseManager as the delegate before your TurnOrderManager would try to call any of the delegate methods.