"Attemped to add a SKNode which already has a parent:" in Repeat Loop. Any simple work around? - swift

I am pretty Newbie to programming. And I am trying to pile up the random blocks dynamically till it hits the upper frame. But it seems that Swift doesn't let me to do so. Did I miss anything please? Any input are appreciated.
let blocks =[block1,block2,block3,block4,block5,block6,block7,block8,block9,block10,block11,block12]
var block:SKSpriteNode!
let blockX:Double = 0.0
var blockY:Double = -(self.size.height/2)
repeat{
block = blocks.randomBlock()
block.zPosition = 2
block.position = CGPoint(x:blockX, y:blockY)
block.size.height = 50
block.size.width = 50
self.addChild(block)
blockY += 50
} while( block.position.y < self.size.height)
extension Array {
func randomBlock()-> Element {
let randint = Int(arc4random_uniform(UInt32(self.count)))
return self[randint]
}
}

you need to have someway of tracking which blocks have been selected and ensure that they don't get selected again. The method below uses an array to store the indexes of selected blocks and then uses recursion to find a cycle through until an unused match is found.
private var usedBlocks = [Int]()
func randomBlock() -> Int {
guard usedBlocks.count != blocks.count else { return -1 }
let random = Int(arc4random_uniform(UInt32(blocks.count)))
if usedBlocks.contains(random) {
return randomBlock()
}
usedBlocks.append(random)
return random
}
in your loop change your initializer to
let index = randomBlock()
if index > -1 {
block = blocks[index]
block.zPosition = 2
block.position = CGPoint(x:blockX, y:blockY)
}
remember that if you restart the game or start a new level, etc. you must clear all of the objects from usedBlocks
usedBlocks.removeAll()

Related

how do i get an average value for scores in a table view (swift)?

I want to display an average for all values that are inputted into an Entity. Basically, one would press add on the TableView and a pop up would appear asking for a score, which would then add that “exam” to the tableview. I want to be able to add up all the scores and receive an average for them which i can then add to a label. I’ve tried following some other tutorial but it gives me an error.
https://i.stack.imgur.com/P7exB.jpg
https://i.stack.imgur.com/WWITI.jpg
The images above are for context.
var i = 0
var sum = 0
for i in 0...methodsExam.count {
let title = methodsExam[i]
let str : String = title.value(forKey: "score") as! String
sum = sum + Int(str)!
}
let avg = sum/methodsExam.count
averageScore.text = "Average: \(avg)"
Assuming methodsExam is an array of [MethodsExam] you can change the loop to
var sum = 0.0
for item in methodsExam {
if let scoreStr = item.score, let score = Double(scoreStr) {
sum += score
}
}
let average = sum / Double(methodsExam.count)
I am using Double here for more precision.
This could also be done with a high-order function
let average = methodsExam.reduce(into: 0.0) {
if let scoreStr = $1.score, let score = Double(scoreStr) { $0 += score }
} / Double(methodsExam.count)
Since we are dividing with the array count we must also check that the array isn't empty to avoid division by 0, so we can put the code into a function and do this
func calculateAverage(_ array: [MethodsExam]) -> Double {
guard !array.isEmpty else { return 0.0 }
return array.reduce(into: 0.0) {
if let scoreStr = $1.score, let score = Double(scoreStr) { $0 += score }
} / Double(array.count)
}
One caveat, I assumed that score can be nil because of MethodsExam being an NSManagedObject and not because it will hold nil values. If it indeed can hold nil values then you need to consider what to calculate the average on, all values in the array or only non-nil values.

Calling random AKPlayers in AKSequencer with repeated random results

This is a multiple audiofile playback project I am currently working on, where multiple AKPlayers are played in a random order, through AKSequencer.
First, I have an array of filenames:
Let filenames = [“1.mp3”, “2.mp3, “3.mp3”, … “10.mp3”]
This is loaded on AKPlayer individually, to later call it in a random order:
Let players: [AKPlayer] = {
Do {
Let filenames = [“1.mp3”, “2.mp3, “3.mp3”, … “10.mp3”]
Return try filenames.map { AKPlayer(audioFile: try AKAudioFile(readRileName: $0)) }
} catch {
fatalError()
}
}()
Then, I called AKPlayer through AKsequencer, by triggering it through ‘playRandom’ function:
Let sequencer = AKSequencer()
Let callbackInst = AKCallbackInstrument()
func playRandom() {
let playerIndex = Int(arc4random_uniform(UInt32(players.count)))
players[playerIndex].play()
}
func addTracks() {
let track = sequencer.newTrack()!
track.add(noteNumber: 48, velocity: 127, position: AKDuration(beats: 0), duration: AKDuration(beats: 16), channel: 0)
track.setMIDIOutput(callbackInst.midiIn)
callbackInst.callback = { status, note, vel in
guard status == .noteOn else { return }
self.playRandom()
}
}
Lastly, I set AKPlayer as AudioKit.output, and started the sequencer.
So far this was successful! The sequencer plays AKPlayer seamlessly, in a random order.
But I wanted to try different kind of randomness: repeating randomly selected player 2 or 3 times. (like 2, 2, 3, 3, 3, 1, 1, 5, 5, 5, 9, 9, …) Right now, ‘playRandom’ simply chooses different AKPlayer on each repeat.
As one solution, thanks to StackOverFlow masters, I tried something like:
class RandomWithRepeats {
var range: ClosedRange<Int>
var repeatRange: ClosedRange<Int>
var repeatCount = 0
var value = 0
init(range: ClosedRange<Int>, repeatRange: ClosedRange<Int>) {
self.range = range
self.repeatRange = repeatRange
}
// generate a random number in a range
// Just use Int.random(in:) with Swift 4.2 and later
func random(in range: ClosedRange<Int>) -> Int {
return Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound + 1))) + range.lowerBound
}
func nextValue() -> Int {
// if repeatCount is 0, its time to generate a new value and
// a new repeatCount
if repeatCount == 0 {
// For Swift 4.2, just use Int.random(in:) instead
value = self.random(in: range)
repeatCount = self.random(in: repeatRange)
}
repeatCount -= 1
return value
}
}
And then I modified playRandom function like:
func playRandom() {
Let rand = randomWithRepeats(range: 1…players.count, repeatRange: 2…3)
Do { players[rand.nextValue()].play() }
}
Turns out, this is the exact same result because the playRandom function itself triggered (by AKSequencer) each repeat, so it doesn’t actually ‘repeat’ random AKPlayer 2 or 3 times. Can I solve this issue in a different way? Much appreciated. <3
The job of the RandomWithRepeats class is to keep track of when to deliver you a different AKPlayer instance, but you are creating a new instance of RandomWithRepeats each time you call playRandom(), so it can't keep track of anything.
Create a single instance of RandomWithRepeats when you create the array of players and use this instance in your playRandom() method.
let rand = RandomWithRepeats(range: 0 ... players.count, repeatRange: 2 ... 3)
func playRandom() {
try? players[rand.nextValue()].play()
}
For a cleaner solution, you might encapsulate the logic of RandomWithRepeats together with the AKPlayer array into a single class, such as a RandomlyRepeatingPlayer class.

Very slow minesweeper recursive algorithm in Swift

I'm working with Swift 3 and Xcode.
I'm creating an iOS game that is basically a Minesweeper, but there are no squares but hexagons, so each hexagon can have up to 6 mines in their surrounding.
I created a recursive algorithm, so that when the player touches an hexagon, if it's not a bomb, then it call a recursive function called "reveal" which :
- if one ore more mine in the surrounding and the touched hexagon is still hidden (by hidden I mean we don't know if it's a mine or not), reveal the hexagon & set the number of surrounding mine's label, and stop the function
- if no mine in the surrounding, for each nearby hexagon that is hidden, call the reveal function.
So here's what my code looks like :
class Hexagon: SKShapeNode
{
var mine: Bool
var hide: Bool
var proximityMines: Int
init(mine: Bool = false, proximityMines: Int = 0, hide: Bool = true)
{
self.mine = mine // if it's a mine
self.proximityMines = proximityMines // number of surrounding mines (that I calculated using a function after I generated the level)
self.hide = hide // if the hexagon is still hidden
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
func reveal(hexagon: Hexagon)
{
if hexagon.proximityMines == 0 && hexagon.hide == true // if there are no mines in the surrounding
{
hexagon.hide = false // we update the value of this hexagon
setNumberLabel(hexagon: hexagon) // we set the .proximityMines number as a label (here 0)
for proxyHexagon in proximityHexagons(hexagon: hexagon) // for each surrounding hexagon ...
{
if proxyHexagon.hide == true // ... that is still hidden
{
reveal(hexagon: proxyHexagon) // we call this function again
}
}
}
else if hexagon.proximityMines != 0 && hexagon.hide == true // else if there are mines in the surrounding
{
hexagon.hide = false // update
setNumberLabel(hexagon: hexagon) // set label
}
}
the proximityHexagons(hexagon: Hexagon) function returns an array containing all surrounding hexagons of a given hexagon.
So I really checked my algorithm again and again, and I really think it's the good one.
But the fact is that when I create a level with 0 or a really low amount of mine, and I click on an hexagon, it takes something like 2 seconds for the recursive function to update all the empty hexagons.
My map contains more or less 260 hexagons, and I debugged the number of calls of reveal() and it's about the same amount.
So why is it taking so much time ? I don't think the iPhone 6 can't handle this amount of operations ! I tried it on my iPhone, not an emulator.
Do you have any idea ?
Ok I've been thinking about this because it sounds like a fun problem. I didn't look up any minesweeper solvers, so I might be way out in left field, but here is how I would approach your problem.
First you have to give every mine an index, and you need to know the pattern of that index such that you can do a little math to get the surrounding indices of every mine. If the rows have identical numbers, and the numbering is sequential across rows, then the surrounding indices are:
[index - 1, index + 1,
index - rowCount, index - rowCount - 1,
index + rowCount, index + rowCount + 1]
Then I would make a class that holds a set of all the safe spots on the map that you had when you built the puzzle. I'll call it SafetyManager.
class SafetyManager {
var safeSpots: Set<Int> = all your safe spots
func indices(surrounding index: Int) -> Set<Int> {
return [index - 1, index + 1,
index - rowCount, index - rowCount - 1,
index + rowCount, index + rowCount + 1]
}
func safePlaces(around hexagon: Int) -> Set<Int> {
let allIndices = indices(surrounding: hexagon)
let safe = allIndices.intersection(safeSpots)
safeSpots.subtract(safe)
return safe
}
}
It's got two important functions, one calculates the surrounding indices, the second filters the safe spots. I'm using sets so we can quickly determine the intersection between the safe spots and the surrounding spots.
Next we need a class that would be instantiated when a move is made so we can do the recursion. Lets call it CheckManager.
class CheckManager {
var checked : [Int]
var unchecked : Set<Int>
init(firstHex: Hexagon, surroundingSafeSpots: Set<Int>) {
checked = [firstHex.index]
unchecked = surroundingSafeSpots
}
func nextUnchecked() -> Int? {
guard !unchecked.isEmpty else { return nil }
let next = unchecked.removeFirst()
checked += [next]
return next
}
func pleaseTake(these indices: Set<Int>) {
unchecked.formUnion(indices)
}
}
You initialize it with your first hexagon, or hex index, and the surrounding safespots that the safety manager would give you, if you get no safe spots from the SafetyManager, no need to instantiate.
It keeps a set of checked spots and unchecked spots. Two important functions, the second you use to give it newly acquired safe spots from the safety manager to be added to the unchecked list. The other returns an optional Int? of the next safe spot to check the surroundings of.
Then to do the recursion, something like this..
func check(spot: Hexagon) {
let safe = safetyMan.safePlaces(around: spot.index)
guard safe.count > 0 else { .. }
let checkMan = CheckManager(firstHex: spot, surroundingSafeSpots: safe)
while let i = checkMan.nextUnchecked() {
let safeSpots = safetyMan.safePlaces(around: i)
checkMan.pleaseTake(these: safeSpots)
} // goes until unchecked is empty
for spot in checkMan.checked {
// get the hex and reveal
}
}
You could keep a dictionary of [Int: Hexagon] to quickly grab the hex for a given index. I haven't tested this so I'm not sure if it works well, or at all or has some improper syntax. It would also probably be a lot faster to use multithreading. Fun problem. Good luck.
Okay, I managed to solve my problem.
The problem was the proximityHexagons function that was taking a lot of time. In fact, each time I called this function, he made 6 complex calculations and added the surrounding hexagons in an array, so it was taking a lot of time.
Here's what it looked like :
func proximityHexagons(hexagon: Hexagon) -> Array<Hexagon>
{
var array = [Hexagon]()
var nodeArray = [[Hexagon]]()
nodeArray.append(nodes(at: CGPoint(x: hexagon.position.x, y: hexagon.position.y + hexagon.height)).filter({$0 is Hexagon}) as! [Hexagon])
nodeArray.append(nodes(at: CGPoint(x: hexagon.position.x + hexagon.width * 3/4, y: hexagon.position.y + hexagon.height / 2)).filter({$0 is Hexagon}) as! [Hexagon])
nodeArray.append(nodes(at: CGPoint(x: hexagon.position.x + hexagon.width * 3/4, y: hexagon.position.y - hexagon.height / 2)).filter({$0 is Hexagon}) as! [Hexagon])
nodeArray.append(nodes(at: CGPoint(x: hexagon.position.x, y: hexagon.position.y - hexagon.height)).filter({$0 is Hexagon}) as! [Hexagon])
nodeArray.append(nodes(at: CGPoint(x: hexagon.position.x - hexagon.width * 3/4, y: hexagon.position.y - hexagon.height / 2)).filter({$0 is Hexagon}) as! [Hexagon])
nodeArray.append(nodes(at: CGPoint(x: hexagon.position.x - hexagon.width * 3/4, y: hexagon.position.y + hexagon.height / 2)).filter({$0 is Hexagon}) as! [Hexagon])
// first, for each 6 directions, I'm adding in an array every nodes that are Hexagon, and then adding all of theses arrays in another bigger one
for node in nodeArray // for each hexagon array in the big array
{
if node.count != 0 // if there is an hexagon
{
array.append(node.first!) // we set the hexagon in the final array
}
}
return array // we return the array containing all surrounding hexagons
}
I prefer checking the surrounding hexagons with the nodes(at: Point) function because my levels aren't always regular maps, they can have a weird positioning and twiz_'s func indices(surrounding index: Int) function could not work.
So I kept my function, but I call it once at the beginning of the level and store in a new variable in my hexagon class all the surrounding hexagons of each hexagon:
class Hexagon: SKShapeNode
{
var mine: Bool
var hide: Bool
var proximityMines: Int
var proxyHexagons: [Hexagon] // here
init(mine: Bool = false, proximityMines: Int = 0, hide: Bool = true, proxyHexagons: [Hexagon] =
[Hexagon]())
{
self.mine = mine
self.proximityMines = proximityMines
self.hide = hide
self.proxyHexagons = proxyHexagons
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And then, in the reveal function, instead of calling the proximityHexagons function, I use the .proxyHexagons array of the hexagon, like this :
func reveal(hexagon: Hexagon)
{
if hexagon.proximityMines == 0 && hexagon.hide == true
{
hexagon.hide = false
setNumberLabel(hexagon: hexagon)
for proxyHexagon in hexagon.proxyHexagons // here
{
if proxyHexagon.hide == true
{
reveal(hexagon: proxyHexagon)
}
}
}
else if hexagon.proximityMines != 0 && hexagon.hide == true
{
hexagon.hide = false
setNumberLabel(hexagon: hexagon)
}
}
And now my function is way faster, I manage to reveal all 260 hexagons in 0.001 secs instead of the old 2.81 secs.

Swift - Array of AVplayers?

I am putting together an app that plays drum beats based on hard-coded arrays as such:
var beatArray = [[String]]()
var beat01 = [["kick"], ["snare"], ["kick"], ["snare"]]
var beat02 = [["kick"], ["kick", "snare"], ["kick"], ["kick","snare"]]
Based on a set of parameters, beat01 or beat02 is chosen and when a button is pressed, an NSTimer is started with this following Selector:
// THIS IS WHAT HAPPENS EVERY BEAT
func increaseCounter() {
beatCounter++
if beatCounter >= beatArray.count {
beatCounter = 0
}
print(beatCounter)
if beatArray[beatCounter].contains("kick") {
kickPlayer.currentTime = 0
kickPlayer.play()
}
if beatArray[beatCounter].contains("snare") {
snarePlayer.currentTime = 0
snarePlayer.play()
}
}
kickPlayer and snarePlayer are AVAudioPlayers.
Since there are only two instruments, separately declaring each IF statement is fine, but as I add more and more instruments, this will soon be chaotic.
Is there a way to streamline this process? I'm looking for something along the lines of:
if beatArray[beatCounter].contains("INSTRUMENT") {
INSTRUMENTPlayer.currentTime = 0
INSTRUMENTPlayer.play()
}
put your players in a map
let players = ["kick": kickPlayer, "snare": snarePlayer, ...]
then
for beat in beatArray[beatCounter] {
if let player = players[beat] {
player.currentTime = 0
player.play()
}
}

How can I create a loop that runs two functions, never overlapping?

I'm new to programming and have been taking online courses in swift and spritekit trying to create my first working game from scratch.
Currently I'm having an issue trying to create a sequence of functions that run independently, wait so that only one is running at a time, and loops indefinitely.
The first function:
func shootTwentyArrows() {
var oneArrow = SKAction.runBlock{
self.shootArrow()
}
var waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
var fireAnArrow = SKAction.sequence([oneArrow, waitBetweenArrows])
self.runAction(SKAction.repeatAction(fireAnArrow, count: 20))
}
And the second function:
func shootSpiral1() {
var leftArrow = SKAction.runBlock{
self.arrowFromLeft()
}
var rightArrow = SKAction.runBlock{
self.arrowFromRight()
}
var waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
var sequence = SKAction.sequence([leftArrow, waitBetweenArrows, rightArrow, waitBetweenArrows])
var spiral1 = SKAction.repeatAction(sequence, count: 5)
self.runAction(spiral1)
to clarify, I'm trying to run something like:
shootTwentyArrows()
when that's done, shootSpiral1(), when that's done repeat.
Thanks in advance for any responses.
I guess the most correct way to do that would be to refactor code a little bit:
func shootTwentyArrows() -> SKAction {
let oneArrow = SKAction.runBlock{
self.shootArrow()
}
let waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
let fireAnArrow = SKAction.sequence([oneArrow, waitBetweenArrows])
return SKAction.repeatAction(fireAnArrow, count: 20)
}
func shootSpiral1() -> SKAction {
let leftArrow = SKAction.runBlock{
self.arrowFromLeft()
}
let rightArrow = SKAction.runBlock{
self.arrowFromRight()
}
let waitBetweenArrows = SKAction.waitForDuration(arrowSpeed)
let sequence = SKAction.sequence([leftArrow, waitBetweenArrows, rightArrow, waitBetweenArrows])
let spiral1 = SKAction.repeatAction(sequence, count: 5)
return spiral1
}
Then somewhere in the code you can just do something like that :
let spiralAction = shootSpiral1()
let oneArrowAction = shootTwentyArrows()
let sequence = SKAction.sequence([spiralAction, oneArrowAction])
let infiniteSequence = SKAction.repeatActionForever(sequence)
self.runAction(infiniteSequence)
I left function names the same on purpose, so you get the idea.
P.S. It is a common practice to declare a variable as let, declare it as var only when you have to modify it later.
The best way to do this is have an SKAction sequence that calls both of your functions. You would call this SKAction from wherever you want to initialize the arrow shooting actions like viewDidLoad for example. This would be the code to call the actions...
var actionShootingArrows = SKAction.sequence([shootSpiral1(), shootTwentyArrows()])
self.runAction(SKAction.repeatActionForever(actionShootingArrows))
Hope this helps!