I'm trying to implement a simple game, in iOS using swift. game is turn bases; first player moves, then second player, then first, ... I'm using a protocol for this:
class PersonAgent: AgentProtocol {
let agentType: AgentType = .person
let color: UIColor
var points: Int = 0
private var tappedLine: Line?
init(color: UIColor) {
self.color = color
}
func action() async -> Line {
while self.tappedLine == nil {}
guard let tappedLine = self.tappedLine else { fatalError() }
self.tappedLine = nil
return tappedLine
}
func tapped(line: Line) {
self.tappedLine = line
}
}
when a user taps on a button, view controller calls to tapped function and after that, action method can return something. the problem is phone is getting over heated for a simple game like this. can I reach same functionality without using while? any suggestion to make things more efficient cpu and memory vise?
for making things more clear, I have this code in my view controller:
extension BoardViewController {
private func game() {
print(#function)
Task {
print("inside task")
while self.shouldResumeGame() {
print("Turn Iteration")
var shouldChangeTurn = true
let currentAgent: AgentProtocol
switch self.turn {
case .first:
currentAgent = self.firstAgent
case .second:
currentAgent = self.secondAgent
}
let actionResult = await currentAgent.action()
// some logic here
}
let message: String
if self.firstAgent.points > self.secondAgent.points {
message = "First Player Won"
} else if self.firstAgent.points < self.secondAgent.points {
message = "Second Player Won"
} else {
message = "Draw"
}
// MARK: Game finished
let alert = UIAlertController(title: "Game Finished", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { [weak self] _ in
self?.navigationController?.popViewController(animated: true)
}))
self.present(alert, animated: true)
}
}
private func shouldResumeGame() -> Bool {
return self.firstAgent.points + self.secondAgent.points != 60
}
}
Don't use a while
loop. This is polling
Modern apps use an event-driven approach where events such as user-interaction or a timer firing trigger actions.
For example, while self.tappedLine == nil {}
will execute millions of times per second.
You haven't given enough information for me to give you a complete answer, but one way of refactoring your PersonAgent
is to use a CheckedContinuation
:
class PersonAgent:AgentProtocol {
let agentType: AgentType = .person
let color: UIColor
var points: Int = 0
var continuation: CheckedContinuation<Line,Never>?
init(color: UIColor) {
self.color = color
}
func action() async -> Line {
guard self.continuation == nil else {
fatalError("Action called while already awaiting")
}
let tappedLine = await withCheckedContinuation { continuation in
self.continuation=continuation
}
self.continuation = nil
return tappedLine
}
func tapped(line: Line) {
self.continuation?.resume(returning:line)
}
}
Now there is no polling for the line. The action
function creates a continuation and awaits for it to be resumed. The tapped
function resumes the continuation with the tapped line.