简体   繁体   English

计算机AI上交UIViewController

[英]Computer AI turns in a UIViewController

In a swift game using UIKit I am writing, a human player will interact with UIKit UIButtons, GUI elements to take actions. 在我正在编写的使用UIKit的快速游戏中,人类玩家将与UIKit UIButtons(GUI元素)交互以采取行动。

In the game, the player will play against AI players. 在游戏中,玩家将与AI玩家对战。

But here's the thing; 但这是事实; the human player presses buttons and interacts and the AI player does not. 人类玩家按下按钮并进行交互,而AI玩家则没有。

Given a simple UIViewController; 给定一个简单的UIViewController;

class SampleViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func buyBtnPressed(_ sender: UIButton) {
       print ("pressed")
    }
}

So what I'm trying to ascertain is, how does the AI player itself take actions and handling turns within the context of the current view controller? 那么我想确定的是,AI播放器本身如何在当前视图控制器的上下文内采取行动并处理转弯?

I believe the best way to do this is that there should be a loop that will wait until all players have completed their respective turns. 我认为最好的方法是应该有一个循环,等待所有玩家完成各自的回合。

But where does this loop go? 但是这个循环在哪里呢? In the view did load? 在视图中加载了吗?

If so, won't it eat up memory, or potentially lead (if not careful) to an endless loop? 如果是这样,它会不会耗尽内存,或者是否可能导致(如果不小心)无休止的循环?

I'm finding it hard to ascertain how an AI player can take actions within the given context of a UIViewController considering GUI elements are for human interaction. 考虑到GUI元素是用于人机交互的,我发现很难确定AI播放器如何在UIViewController的给定上下文中采取行动。

I don't mean the AI should be animating pressing buttons or interacting with the screen, I mean; 我的意思不是说AI应该为按下按钮设置动画或与屏幕互动。 I have a UIViewController, it has a view did load; 我有一个UIViewController,它的视图确实已加载; what is the strategy of implementing AI turns and whether or not this should be be achieved in a "game loop" in the View did load or can this be achieved in another way? 实现AI转弯的策略是什么?是否应该在加载了View的“游戏循环”中实现它,或者是否可以通过其他方式实现?

My question is; 我的问题是; given the context of a UIViewController; 给定一个UIViewController的上下文; how can I code the handling of an AI player taking turns and can this be achieved with a loop or another strategy? 我该如何编码AI玩家轮流使用的处理方式,并且可以通过循环或其他策略来实现?

Many thanks 非常感谢

edit: Code is now added 编辑:现在已添加代码

I have written out a turn base manager using Swift playgrounds, and 2 examples one using a UIViewController and another is a loop. 我已经写了一个使用Swift游乐场的转弯基地经理,还有2个示例,一个使用UIViewController,另一个是循环。

code now follows; 现在代码如下;

import Foundation
import GameplayKit

class Player {
    var name: String
    public private(set) var isAI: Bool = false
    public private(set) var turnOrder: Int = 0

    init(name: String, isAI: Bool?) {
        self.name = name
        if let hasAI = isAI {
            self.isAI = hasAI
        }
    }

    func setTurnOrderIndex(number: Int) {
        self.turnOrder = number
    }
}

let p1 = Player.init(name: "Bob", isAI: false)
let p2 = Player.init(name: "Alex", isAI: true)

protocol TurnOrderManagerDelegate: NSObjectProtocol {
    func turnOrderWasSet()
}

protocol TurnDelegate: class {
    func turnIsCompleted()
}

class Turn: NSObject {
    weak var player: Player?
    weak var delegate: TurnDelegate?

    public private(set) var completed: Bool = false {
        didSet {
            delegate?.turnIsCompleted()
        }
    }

    init(player:Player, delegate: TurnDelegate) {
        self.player = player
        self.delegate = delegate
    }

    func setAsComplete() {
        self.completed = true
    }
}

class TurnOrderManager: NSObject, TurnOrderManagerDelegate, TurnDelegate {
    static var instance = TurnOrderManager()

    public private(set) var turnOrderIndex: Int = 0
    public private(set) var turnOrder: [Turn] = [Turn]() {
        didSet {
            self.turnOrderWasSet()
        }
    }

    var playerOnTurn: Player? {
        let turnObj = self.turnOrder[turnOrderIndex]
        return (turnObj.player)
    }

    var allTurnsCompleted: Bool {
        let filtered = turnOrder.filter { (turnObj:Turn) -> Bool in
            return (turnObj.completed)
        }.count
        return (filtered == turnOrder.count)
    }

    func setTurnOrder(players:[Player]) {
        if (self.turnOrder.count == 0) {
            for playerObj in players {
                let turnObj = Turn.init(player: playerObj, delegate: self)
                self.turnOrder.append(turnObj)
            }
        }
    }

    func turnOrderWasSet() {
        for (index, turnObj) in self.turnOrder.enumerated() {
            turnObj.player?.setTurnOrderIndex(number: index)
        }
    }

    func next() {
        if (turnOrderIndex < (self.turnOrder.count - 1)) {
            turnOrderIndex += 1
        }
        else {
            turnOrderIndex = 0
        }
    }

    internal func turnIsCompleted() {
        print (" - turnIsCompleted")
        TurnOrderManager.instance.next()
    }

}

class GameModel {
    var turnOrderManager: TurnOrderManager

    init() {
        self.turnOrderManager = TurnOrderManager.instance
        self.turnOrderManager.setTurnOrder(players:[p1,p2])
    }

    // other game model stuff [...]
}

class Phase1State : GKState {

    var gameModel: GameModel!

    init(gameModel:GameModel) {
        super.init()
        self.gameModel = gameModel
    }

    override func isValidNextState(_ stateClass: AnyClass) -> Bool
    {
        return false
    }

    override func didEnter(from previousState: GKState?) {
    }

    override func willExit(to nextState: GKState) {
    }

    // MARK: - Action

    func buy() {
        let index = self.gameModel.turnOrderManager.turnOrderIndex
        let turn = self.gameModel.turnOrderManager.turnOrder[index]
        turn.setAsComplete()
    }
}

class SomeViewController: UIViewController
{
    var gameModel: GameModel?
    weak var gamePhase: Phase1State?
    var isPhaseComplete: Bool {
        return self.gameModel?.turnOrderManager.allTurnsCompleted ?? false
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.gameModel = GameModel.init()
        self.gamePhase = Phase1State.init(gameModel: self.gameModel!)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func buyButtonPressed() {
        self.gamePhase?.buy()
        self.finishTurn()
    }

    func finishTurn() {
        guard let turnIndex = self.gameModel?.turnOrderManager.turnOrderIndex else {
            return
        }
        guard let turn = self.gameModel?.turnOrderManager.turnOrder[turnIndex] else {
            return
        }
        turn.setAsComplete()

        if (self.isPhaseComplete)
        {
            print ("All turns are completed")
        }
        else {
            //self.gameModel?.turnOrderManager.next()
            self.gamePhase?.buy()

            guard let playerOnTurn = self.gameModel?.turnOrderManager.playerOnTurn else {
                print ("No player is on turn")
                return
            }

            print ("\(playerOnTurn.name) is on turn")

            if (playerOnTurn.isAI)
            {
                self.gamePhase?.buy()
                self.finishTurn()
            }
        }
    }
}


// EXAMPLE 1 -- first attempt ...

let vc = SomeViewController()
vc.viewDidLoad()
vc.buyButtonPressed()

// EXAMPLE 2 -- another attempt ....


let gameModel: GameModel = GameModel.init()
let gamePhase = Phase1State.init(gameModel: gameModel)

// player then takes an action

while (gameModel.turnOrderManager.allTurnsCompleted == false)
{
    let turnIndex = gameModel.turnOrderManager.turnOrderIndex
    let turnObj = gameModel.turnOrderManager.turnOrder[turnIndex]
    guard let playerOnTurn = turnObj.player else {
        break
    }

    print ("Player \(playerOnTurn.name) is on turn")

    gamePhase.buy()
}

print ("All turns are completed, advance to next phase")

The issue is; 问题是;

On the finishTurn, it only seems to work if it relies on the first player in the index is a human player. 在finishTurn上,只有依靠索引中的第一个玩家为人类玩家时,它才似乎起作用。 If its not, I have no idea how to make it fire the buy action. 如果不是这样,我不知道如何触发购买行为。

On the second example, I use a loop; 在第二个示例中,我使用一个循环。 but I'm concerned using a loop could end up just looping forever. 但我担心使用循环可能会永远循环下去。

My query is therefore clarifyed, how can I ensure my view controller will fire actions for AI players when they don't press buttons and loop through each player and execute their respective turn. 因此澄清了我的查询,如何确保我的视图控制器在AI玩家不按下按钮并在每个玩家中循环并执行各自的回合时为他们触发动作。

Many thanks 非常感谢

Further edit: 进一步编辑:

I do not know if I should have the while (gameModel.turnOrderManager.allTurnsCompleted == false) loop inside my viewDidLoad() to act like a game loop. 我不知道我的viewDidLoad()内是否应该有while (gameModel.turnOrderManager.allTurnsCompleted == false)循环来像游戏循环一样。

Look, I'm not sure on what kind of game you're making, buy you should probably learn SpriteKit , specially SKActions . 看,我不确定您在制作哪种游戏,请您可能应该学习SpriteKit ,尤其是SKActions With that, you can easily control the flow of events from your game. 这样,您可以轻松控制游戏中事件的流程。

With that said, how is your AI implementation? 话虽如此,您的AI实施情况如何? Based on your code, I would begin with something like this: 根据您的代码,我将从以下内容开始:

class AI {

    enum Decision {
        case doSomething
        case doAnotherThing
        case dontDoAnything
    }

    public func decide() -> Decision {
        // Decide which action the AI will take...
        return .doSomething // This return is just a example!
    }

    public func act(on : Decision) {
        // Do whatever the AI needs based on a decision...
    }

}

Then, in your ViewController: 然后,在您的ViewController中:

class SampleViewController: UIViewController {

    var ai = AI()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func buyBtnPressed(_ sender: UIButton) {
        print ("pressed")
        ai.act(on: ai.decide())
    }

}

I hope that helps! 希望对您有所帮助!

There is no need to specifically use Sprite Kit for this. 无需为此专门使用Sprite Kit。 SpriteKit would be more to do with how the UI is made rather than how the logic of the game works. SpriteKit将更多地与UI的制作方式有关,而不是游戏逻辑的运作方式。

However, I would recommend looking at GameplayKit. 但是,我建议您查看GameplayKit。 It's a framework that contains lots of built in game logic tools. 它是一个包含许多内置游戏逻辑工具的框架。 Specifically you want something like the GKDecisionTree . 具体来说,您想要类似GKDecisionTree东西。 There are a few WWDC videos about it too. 也有一些WWDC视频。 GameplayKit can be used with SpriteKit, UIKit, SSceneKit or any other game engine that you decide to use (or not). GameplayKit可以与SpriteKit,UIKit,SSceneKit或您决定使用(或不使用)任何其他游戏引擎一起使用。

Also, the question you're asking is a very general question about game development. 另外,您要问的问题是关于游戏开发的一个非常普遍的问题。 Having the computer "decide" to do something is quite a complex subject. 让计算机“决定”做某事是一个相当复杂的主题。

I'd also suggest having a quick watch of this video from AI & Games and other videos from that channel. 我还建议您快速观看AI&Games的视频以及该频道的其他视频。

It'll give you an idea of how to approach your problem. 它会给您一个解决问题的思路。

Session 609 and 608 from WWDC 2015 and 2016 are prob good :D WWDC 2015和2016的会议609和608可能不错:D

Regarding updating the AI. 关于更新AI。

Your AI should be event driven. 您的AI应该是事件驱动的。 You have the concept of "turns" and "players". 您具有“转弯”和“玩家”的概念。 There is a point in the game at which it becomes a "player's" "turn". 游戏中有一个地方变成“玩家的”“转身”。 (Even at the very beginning of the game it is either Player 1 or Player 2's turn. (即使在游戏开始时,也就是玩家1或玩家2的回合。

At this time there are two possibilities. 这时有两种可能性。 Either the player is an AI, or the player is a person. 玩家是AI还是玩家是人。

As soon as this happens there should be some sort of trigger (like a function call or something) that tells the player its turn has started. 一旦发生这种情况,就应该有某种触发器(例如函数调用之类的东西)告诉玩家轮到其开始了。

If that player is the AI then you need to start some sort of calculation (maybe with a built in delay to make it realistic) so that it decides what to do. 如果该玩家是AI,则您需要开始某种计算(可能需要内置延迟才能使它变得现实),以便它决定要做什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM