Im trying to create a robots challenge board game, but im really struggling with the round and thread logic.
Some suppositions:
1- Its a 7x7 board game
2- At the start of each round, randomly place a prize token somewhere on the game board and start each robot at opposite corners of the board.
3 - As each move is made the robot will leave a trail of each space it has moved through during the round — these trail spaces may not be passed through by the other robot during the course of the round.
4 - Each robot may move either left, right, up, or down to an unoccupied and unvisited space. Diagonal moves are not allowed.
5 - If a robot cannot move from its current position, it should hold its position until the end of the round.
6 - The first robot to reach the prize wins one point, the board then resets, and the next round starts. This simulation should run continuously, keeping track of the total score for each robot during the game session.
7 - Each robot should run concurrently in separate threads but it maintaining turn order.
and this is my current code:
enum BoardCellType {
case empty
case robot1
case robot2
case prize
}
struct Position: Equatable, Hashable {
var x: Int
var y: Int
static func == (lhs: Position, rhs: Position) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
class Robot: Equatable {
let uuid: UUID
var position: Position {
didSet {
visitedPositions.append(position)
}
}
var score: Int
var hasToWait: Bool = false
var totalMoves: Int
var visitedPositions: [Position]
init(position: Position, score: Int = 0, totalMoves: Int = 0, visitedPositions: [Position] = []) {
self.uuid = UUID()
self.position = position
self.score = score
self.totalMoves = totalMoves
self.visitedPositions = visitedPositions
}
func getPossibleMoves() -> [Position] {
let moves = [Position(x: position.x + 1, y: position.y),
Position(x: position.x - 1, y: position.y),
Position(x: position.x, y: position.y + 1),
Position(x: position.x, y: position.y - 1)]
return moves
}
static func == (lhs: Robot, rhs: Robot) -> Bool {
return lhs.uuid == rhs.uuid
}
}
protocol GameControllerDelegate: AnyObject {
func gameDidUpdate()
func showEndGameAlert(message: String)
}
class GameController {
var board: [[BoardCellType]]
let boardSize: Int
var roundNumber: Int
var prizePosition: Position
var robot1: Robot
var robot2: Robot
var robot1Path: [Position]
var robot2Path: [Position]
var round: Int
var moves: Int
var currentPlayer: Robot?
weak var delegate: GameControllerDelegate?
private let robot1Queue = DispatchQueue(label: "com.robot1.queue")
private let robot2Queue = DispatchQueue(label: "com.robot2.queue")
private let gameQueue = DispatchQueue(label: "com.game.queue")
var gameTimer: Timer?
init(boardSize: Int) {
self.boardSize = boardSize
self.roundNumber = 0
self.board = Array(repeating: Array(repeating: .empty, count: boardSize), count: boardSize)
self.robot1 = Robot(position: Position(x: 0, y: 0), score: 0)
self.robot2 = Robot(position: Position(x: boardSize - 1, y: boardSize - 1), score: 0)
self.robot1Path = []
self.robot2Path = []
self.round = 0
self.moves = 0
self.prizePosition = Position(x: Int.random(in: 0..<boardSize), y: Int.random(in: 0..<boardSize))
}
func startGame() {
roundNumber += 1
resetRobots()
currentPlayer = robot1
resetBoard()
play()
}
private func resetBoard() {
for i in 0..<boardSize {
for j in 0..<boardSize {
board[i][j] = .empty
}
}
board[robot1.position.x][robot1.position.y] = .robot1
board[robot2.position.x][robot2.position.y] = .robot2
board[prizePosition.x][prizePosition.y] = .prize
robot1Path = [robot1.position]
robot2Path = [robot2.position]
}
private func resetRobots() {
var positions = Set<Position>()
while positions.count < 3 {
let randomPosition = Position(x: Int.random(in: 0..<boardSize), y: Int.random(in: 0..<boardSize))
positions.insert(randomPosition)
}
let positionsArray = Array(positions)
robot1 = Robot(position: positionsArray[0], visitedPositions: [positionsArray[0]])
robot2 = Robot(position: positionsArray[1], visitedPositions: [positionsArray[1]])
prizePosition = positionsArray[2]
}
func isMoveValid(position: Position, robot: Robot) -> Bool {
// Check bounds
if position.x < 0 || position.y < 0 || position.x >= boardSize || position.y >= boardSize {
return false
}
// Check if position is already visited by the robot itself
if robot.visitedPositions.contains(position) {
return false
}
// Check if position is already visited by the other robot
let otherRobot = (robot == robot1) ? robot2 : robot1
if otherRobot.visitedPositions.contains(position) {
return false
}
return true
}
func checkGameEnded() -> Bool {
if !getPossibleMoves(robot: robot1).isEmpty || !getPossibleMoves(robot: robot2).isEmpty {
return false
}
return true
}
func getPossibleMoves(robot: Robot) -> [Position] {
return robot.getPossibleMoves().filter { isMoveValid(position: $0, robot: robot) }
}
func moveRobot(_ robot: Robot) {
// If the robot has to wait, don't attempt to move it
guard !robot.hasToWait else {
checkGameStatus()
print("robot has to Wait \(currentPlayer)")
return
}
// get current robot =
// create next robot move
let direction = Int.random(in: 0...3)
var newX = robot.position.x
var newY = robot.position.y
switch direction {
case 0: // Move left
newX -= 1
case 1: // Move right
newX += 1
case 2: // Move up
newY -= 1
default: // Move down
newY += 1
}
let newPosition = Position(x: newX, y: newY)
let type: BoardCellType = robot == robot1 ? .robot1 : .robot2
if isMoveValid(position: newPosition, robot: robot) {
robot.position = newPosition
// save on the robot path the newPosition
if robot == self.robot1 {
self.robot1Path.append(newPosition)
} else if robot == self.robot2 {
self.robot2Path.append(newPosition)
}
//Checks if the robot got the prize
if self.board[newPosition.x][newPosition.y] == .prize {
print("robot got the prize \(type)")
robot.score += 1
// Update the board after the robot gets the prize
updateBoard(position: newPosition, type: .empty)
endGame()
} else {
print("simple move \(type)")
// update the board position
updateBoard(position: newPosition, type: type)
robot.totalMoves += 1
// Check if the robot can't move anymore
if self.getPossibleMoves(robot: robot).isEmpty {
robot.hasToWait = true
}
}
self.delegate?.gameDidUpdate()
self.checkGameStatus() // Check if the game has ended
} else {
print("invalid move \(type)")
robot.hasToWait = true
self.delegate?.gameDidUpdate()
self.checkGameStatus() // Check if the game has ended
}
print("robot1 \(robot1.visitedPositions)")
print("robot2 \(robot2.visitedPositions)")
// Check if the game has ended due to both robots having no valid moves
if checkIfGameEnded() {
endGame()
} else {
currentPlayer = (currentPlayer == robot1) ? robot2 : robot1
playRound()
}
}
private func updateBoard(position: Position, type: BoardCellType) {
self.board[position.x][position.y] = type
}
private func checkIfGameEnded() -> Bool {
return getPossibleMoves(robot: robot1).isEmpty && getPossibleMoves(robot: robot2).isEmpty
}
func checkGameStatus() {
if robot1.hasToWait && robot2.hasToWait || checkGameEnded() {
self.endGame()
} else {
self.currentPlayer = (self.currentPlayer == self.robot1) ? self.robot2 : self.robot1
self.play()
}
}
private func moveRobotOnThread(_ robot: Robot, queue: DispatchQueue, completion: @escaping () -> Void) {
queue.asyncAfter(deadline: .now() + 2.0) {
self.moveRobot(robot)
DispatchQueue.main.async {
completion()
}
}
}
func play() {
playRound()
}
func playRound() {
guard let currentPlayer = currentPlayer else {
return
}
let queue = (currentPlayer == robot1) ? robot1Queue : robot2Queue
moveRobotOnThread(currentPlayer, queue: queue) { [weak self] in
self?.delegate?.gameDidUpdate()
self?.checkGameStatus()
}
}
func endGame() {
// Reset the board
// Print the result of the current round
print("Round \(roundNumber):")
print("Robot 1 score: \(robot1.score), total moves: \(robot1.totalMoves)")
print("Robot 2 score: \(robot2.score), total moves: \(robot2.totalMoves)")
// Determine the end condition and show the alert
var message = ""
if robot1.hasToWait && robot2.hasToWait {
message = "End of moves for both robots."
} else if robot1.hasToWait {
message = "Robot 1 can't move anymore."
} else if robot2.hasToWait {
message = "Robot 2 can't move anymore."
} else if robot1.score > robot2.score {
message = "Robot 1 wins!"
} else if robot2.score > robot1.score {
message = "Robot 2 wins!"
} else {
message = "It's a draw!"
}
delegate?.gameDidUpdate()
delegate?.showEndGameAlert(message: message)
resetBoard()
}
}
The problem is, I can't make the code run each robot on a separated thread and work by turns. Already tried with NSTimer, but I think its not the purpose of the challenge>