如何使用Swift的Gameplaykit中的GKRandomSource“播种”以记住会话之间的混乱

时间:2016-02-03 12:30:37

标签: swift gameplay-kit

我是编程新手,通过大量的在线课程学习Swift。在其中一门课程中我们建立了一个基本的琐事游戏,我一直在逐步尝试通过自己的编码来改进它(最好的学习方法!)。

最近我遇到了所谓的Fisher-Yates shuffle,经过多次试验和错误(并且在堆栈溢出社区的帮助下)能够使用Swift的Gameplaykit中的GKRandomSource来改变我的琐事问题,以便它们是随机被问到。这是我使用的原始arc4随机代码的改进,因为shuffle已经从整个问题池中删除了问题,从而确保它们不会重复(至少在iOS9中)。

这在会话中运行良好,但是一旦用户退出应用并重新启动它,则随机启动随机播放。所以我正在寻找一种让应用程序“记住”会话之间已经提出的问题的方法。我的研究让我想到了播种的想法,我一直在尝试使用我的GKRandomSource代码,但我显然遗漏了一些东西。

任何建议等都是最受欢迎的 - 特别是因为我不完全确定这种“播种”方法将达到我的最终目标,即不重复在应用程序之前的会话中已经提出的问题。

以下是我认为是我修订代码的相关内容。

所有问题和潜在答案选择都存储在.json文件中:

{
        "id" : "1",
        "question": "Earth is a:",
             "answers": [
            "Planet",
            "Meteor",
            "Star",
            "Asteroid"
          ],
          "difficulty": "1"
      }

我使用以下代码加载.json文件:

func loadAllQuestionsAndAnswers()
{
    let path = NSBundle.mainBundle().pathForResource("content", ofType: "json")
    let jsonData : NSData = NSData(contentsOfFile: path!)!
    allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
    //println(allEntries)

}

以下是我最近的代码,试图实现所有问题的改组,并在以后的会话中复制它:

var allEntries : NSArray!
var shuffledQuestions: [AnyObject]!
var nextQuestion = -1


    var mySeededQuestions : [AnyObject]
    loadAllQuestionsAndAnswers()

    if #available(iOS 9.0, *) {
            let lcg = GKLinearCongruentialRandomSource(seed: mySeededQuestions)
            let shuffledQuestions = lcg.arrayByShufflingObjectsInArray(allEntries)
            nextQuestion++
            loadQuestion(nextQuestion)

            // Fallback on earlier versions

        }else{

            let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))
            loadQuestionPreiOS9(randomNumber)

        }

我至少知道上面的代码有问题,但我很茫然。我也在想,也许我在存储种子方面缺少一步?

为了完整起见,我使用标签显示问题,并使用以下代码显示四个图像以显示潜在答案:

func loadQuestion(index : Int)
{
    let entry : NSDictionary = shuffledQuestions[index] as! NSDictionary
    let question : NSString = entry.objectForKey("question") as! NSString
    let arr : NSMutableArray = entry.objectForKey("answers") as! NSMutableArray

    //println(question)
    //println(arr)

    labelQuestion.text = question as String

    let indices : [Int] = [0,1,2,3]
    //let newSequence = shuffle(indices)
    let newSequence = indices.shuffle()
    var i : Int = 0
    for(i = 0; i < newSequence.count; i++)
    {
        let index = newSequence[i]
        if(index == 0)
        {
            // we need to store the correct answer index
            currentCorrectAnswerIndex =  i

        }

        let answer = arr.objectAtIndex(index) as! NSString
        switch(i)
        {
        case 0:
            buttonA.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        case 1:
            buttonB.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        case 2:
            buttonC.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        case 3:
            buttonD.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        default:
            break;
        }



    }
    buttonNext.hidden = true
    // we will need to reset the buttons to reenable them
    ResetAnswerButtons()

}


func loadQuestionPreiOS9(index : Int)
{
    let entry : NSDictionary = allEntries.objectAtIndex(index) as! NSDictionary
    let question : NSString = entry.objectForKey("question") as! NSString
    let arr : NSMutableArray = entry.objectForKey("answers") as! NSMutableArray

    //println(question)
    //println(arr)

    labelQuestion.text = question as String

    let indices : [Int] = [0,1,2,3]
    //let newSequence = shuffle(indices)
    let newSequence = indices.shuffle()
    var i : Int = 0
    for(i = 0; i < newSequence.count; i++)
    {
        let index = newSequence[i]
        if(index == 0)
        {
            // we need to store the correct answer index
            currentCorrectAnswerIndex =  i

        }

        let answer = arr.objectAtIndex(index) as! NSString
        switch(i)
        {
        case 0:
            buttonA.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        case 1:
            buttonB.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        case 2:
            buttonC.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        case 3:
            buttonD.setTitle(answer as String, forState: UIControlState.Normal)
            break;

        default:
            break;
        }



    }
    buttonNext.hidden = true
    // we will need to reset the buttons to reenable them
    ResetAnswerButtons()

}

最后,我使用以下代码在用户回答完问题后向其显示“下一步”按钮:

@IBAction func PressedButtonNext(sender: UIButton) {
    print("button Next pressed")

    if #available(iOS 9.0, *) {
    nextQuestion++
    loadQuestion(nextQuestion)
    }else{
        let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))
        loadQuestionPreiOS9(randomNumber)
    }

我知道我的编码可能非常冗长和不必要,但直到最新改进它一直工作正常,我实际上理解它(我想!)

2 个答案:

答案 0 :(得分:2)

这里有两个问题:你在询问什么以及你想要什么。因为不同的原因,他们都值得回答,所以......

如何播种GK(随便)RandomSource

(所有GKRandomSource子类都有种子,即使超类GKRandomSource本身不存在......这是因为每个类都有自己的种子数据类型。但用法是相同的。 )

由于类型不匹配,您发布的代码的关键位甚至无法编译:{{1​​}}的{​​{1}} / seed值是整数,而不是数组对象该值的文档说明了它的含义(强调添加)以及如何使用它:

  

使用相同种子数据初始化的任何两个随机源都将生成相同的随机数序列。要复制现有init(seed:)实例的行为,请读取该实例的{{1}通过将结果数据传递给GKLinearCongruentialRandomSource初始值设定项,然后创建一个新实例。

所以,如果你想复制一系列随机数:

  1. 使用普通初始值设定项创建随机源。

    GKLinearCongruentialRandomSource
  2. 保存该来源的seed值。

    initWithSeed:
  3. 随意使用该随机源。

  4. 稍后,当您再次启动并想要相同的序列时,请读取种子值并使用种子创建随机源。

    let source = GKLinearCongruentialRandomSource()
    
  5. 但仍然无法获得您实际需要的内容:如果步骤1中的seed生成序列let seed = source.seed // -> some UInt64 value // write seed to user defaults, a file, a web service, whatever. ,则步骤4中的let seed = // read in seed value from wherever you saved it let source = GKLinearCongruentialRandomSource(seed: seed) 也会产生序列source - 种子不记录序列中“离开”的位置。或者,因为你将它用于数组shuffle,它将产生与第一个shuffle相同的数组洗牌顺序,但是它不记得你之后用洗牌数组做了什么。

    如何在多个应用启动中使用随机排序

    如果您想要对数组进行随机播放,请按顺序遍历它,然后在稍后运行的应用程序中继续浏览您从中断处停止的同一个混洗数组,您需要围绕该要求进行设计。

    1. 第一次发布时随机播放。

    2. 记录有关所产生的订单的信息。 (比如,shuffle中的索引到原始数据中的索引的映射。)

    3. 走过洗牌后的阵列,记录你走过的距离。

    4. 在以后的应用程序运行中,使用订购记录和进度记录来确定您的位置。

    5. 这是一个粗略的传球。 (请注意,我没有触及您的数据模型 - 这是一个程序设计问题,而且SO不是编码服务。您需要考虑如何充实这个设计以匹配您的模型及其用例。)

      1, 6, 3, 9, 2, 7

      这可能有点伪代码(因此您可能需要担心将数组转换为与source等一起使用),但作为一般设计,它应该足以让您有所思考。< / p>

答案 1 :(得分:0)

您还可以使用以下内容来减少一定数量的值,因此,如果您保持滚动计数在下一次开始时就减少那么多就很容易了:

.asc