Generate random values based on a seed Swift 3 - swift

lets say you were making a game and this game has procedurally generated terrain from a seed that the user inputted in his world-creation menu
and this seed generates a set of values that only change if the seed changes
for instance lets say you want to get a random set of integers to generate in a for-loop and pull randomly from an array the set of integers stay the same every time and pull the same items from the array in the exact same order every time you run the for-loop until you change the seed
how would one achieve this in swift
in my code here i can get the terrain to spawn in but it does not generate the same every time which is what i am trying to get it to do
override func didMove(to view: SKView) {
let tile1 = SKTileDefinition(texture: SKTexture(imageNamed: "stone") ,size: CGSize(width: 64, height: 64))
let tile2 = SKTileDefinition(texture: SKTexture(imageNamed: "water") ,size: CGSize(width: 64, height: 64))
let tile3 = SKTileDefinition(texture: SKTexture(imageNamed: "sand") ,size: CGSize(width: 64, height: 64))
let tile4 = SKTileDefinition(texture: SKTexture(imageNamed: "grass") ,size: CGSize(width: 64, height: 64))
let tileGroup1 = SKTileGroup(tileDefinition: tile1)
let tileGroup2 = SKTileGroup(tileDefinition: tile2)
let tileGroup3 = SKTileGroup(tileDefinition: tile3)
let tileGroup4 = SKTileGroup(tileDefinition: tile4)
let tileGroup5 = SKTileGroup(tileDefinition: tile4)
let tileGroup6 = SKTileGroup(tileDefinition: tile4)
let tileSet = SKTileSet(tileGroups: [tileGroup1,tileGroup2,tileGroup3,tileGroup4,tileGroup5,tileGroup6])
let columns = 5
let rows = 5
let tileSize = CGSize(width: 64, height: 64)
//this is another GKNoise class called Noise
let noise = Noise()
let noiseMap = GKNoiseMap(noise, size: vector_double2(10.0,10.0), origin: vector_double2(0.0,0.0), sampleCount: vector_int2(100), seamless: true)
//this is another SKTileMapNode Class called TileMap and a class func called tileMapNodes
let tileMap = TileMap.tileMapNodes(tileSet: tileSet, columns: columns, rows: rows, tileSize: tileSize, from: noiseMap, tileTypeNoiseMapThresholds: [(-1.0 as NSNumber),(+1.0 as NSNumber)])
tileMapNode = tileMap.first!
let seed = Int
for column in 0 ..< tileMapNode.numberOfColumns {
for row in 0 ..< tileMapNode.numberOfRows {
let rand = Int(arc4random_uniform(UInt32(tileSet.tileGroups.count)))
print(rand)
let tile = tileMapNode.tileSet.tileGroups[rand]
tileMapNode.setTileGroup(tile, forColumn: column, row: row)
}
}

If you want to generate a random value based on a specific seed use: srand48 and drand48:
srand allow you to specify the seed:
srand48(230)
Then drand48 will give you a double between 0 and 1:
let doubleValue = drand48()
So in your case, if you want an Int between 0 and 9, you can do something like:
srand48(230)
print("Random number: \(1 + Int((drand48() * 8) + 0.5))")
print("Random number: \(1 + Int((drand48() * 8) + 0.5))")
print("Random number: \(1 + Int((drand48() * 8) + 0.5))")
It will give you the 3 pseudo random numbers until you change the seed
Here is the drand48 applied to your new question:
let seed = srand48(230)
for column in 0 ..< tileMapNode.numberOfColumns {
for row in 0 ..< tileMapNode.numberOfRows {
let rand = Int(Double(tileSet.tileGroups.count) * drand48() - 0.5)
print(rand)
let tile = tileMapNode.tileSet.tileGroups[rand]
tileMapNode.setTileGroup(tile, forColumn: column, row: row)
}
}

Related

memory issue: realtime eye localization

I'm currently working on a iOS-application, which should be able to detect the localization. I've created an tflite which comprises some CNN layers. In order to use the tflite in XCode/Swift I've created a helper class in which the tflite calculates the output. Whenever I run the predict-function once, it works. But apparently the predict function doesn't work in real-time camera-thread.
After about 7 seconds, XCode is throwing the following error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x123424001). This error must be evoken by looping through the image. Since I need each pixel value I'm using the solution suggested by Firebase.But apparently this solution is not waterproofed. Can anybody help to resolve this memory issue?
func creatInputForCNN(resizedImage: UIImage?) -> Data{
// In this section of the code I loop through the image (150,200,3)
// in order to fetch each pixel value (RGB).
let image: CGImage = resizedImage.cgImage!
guard let context = CGContext(
data: nil,
width: image.width, height: image.height,
bitsPerComponent: 8, bytesPerRow: image.width * 4,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
) else {return nil}
context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
guard let imageData = context.data else {return nil}
let size_w = 150
let size_H = 200
var inputData:Data?
inputData = Data()
for row in 0 ..< size_H{
for col in 0 ..< size_w {
let offset = 4 * (row * context.width + col)
// (Ignore offset 0, the unused alpha channel)
let red = imageData.load(fromByteOffset: offset+1, as: UInt8.self)
let green = imageData.load(fromByteOffset: offset+2, as: UInt8.self)
let blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self)
// Normalize channel values to [0.0, 1.0]. This requirement varies
// by model. For example, some models might require values to be
// normalized to the range [-1.0, 1.0] instead, and others might
// require fixed-point values or the original bytes.
var normalizedRed:Float32 = Float32(red) / 255
var normalizedGreen:Float32 = (Float32(green) / 255
var normalizedBlue:Float32 = Float32(blue) / 255
// Append normalized values to Data object in RGB order.
let elementSize = MemoryLayout.size(ofValue: normalizedRed)
var bytes = [UInt8](repeating: 0, count: elementSize)
memcpy(&bytes, &normalizedRed, elementSize)
inputData!.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedGreen, elementSize)
inputData!.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedBlue, elementSize)
inputData!.append(&bytes, count: elementSize)
return inputData
}
This is the code

IOS Charts positioning of gridlines

I am struggling to find out how to set gridlines on my chart to specific points on the x axis and to centre labels on the lines corresponding to the time of day. I have a simple chart, the data represents a day with about 280 data points (one for every 5 minute period). I want to show gridlines at times of the day equal to 03:00, 06:00, 12:00, 15:00, 18:00 and 21:00 and show the corresponding labels centred below the gridlines. I have have also tried LimitLines, which works for placing the vertical lines at the correct points, but there does not seem to be the ability to align the time labels with the centre of the limit lines.
I know this can be done because I have seen screenshots on-line showing time labels centred on gridlines (or limitlines) for specific periods on the day, but I've spent hours looking for how to do this an I'm drawing a blank.
My code below (note the commented out failed attempt to get limitlines to work).
let tideHeights = LineChartDataSet(entries: dataEntries)
tideHeights.drawCirclesEnabled = false
tideHeights.colors = [NSUIColor.black]
//let timings = ["00:00", "03:00", "06:00", "12:00", "15:00", "18:00", "21:00"]
let data = LineChartData(dataSet: tideHeights)
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
//lineChartView.xAxis.drawGridLinesEnabled = false
//let gridLine = values.count / timings.count
//var xPos = 0
//for label in timings {
//let limitLine = ChartLimitLine(limit: Double(xPos), label: label)
//limitLine.labelPosition = .bottomLeft
//limitLine.lineWidth = 0.5
//limitLine.lineColor = .black
//limitLine.valueTextColor = .black
//lineChartView.xAxis.addLimitLine(limitLine)
//xPos += gridLine
//}
lineChartView.xAxis.labelCount = 7
lineChartView.xAxis.labelPosition = .bottom
lineChartView.xAxis.granularity = 1
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.legend.enabled = false
lineChartView.data = data
The output is below, the gridlines are at system set positions, how can I control where they are placed?
Update:
After applying the very helpful suggestion by Stephan Schlecht, I am still getting strange results. With lineChartView.xAxis.setLabelCount(9, force: true) I get:
and with lineChartView.xAxis.setLabelCount(4, force: true) I get:
It seems for some reason that the 1st gridline after the Axis is ignored.
Code is:
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
let marker = ChartMarker()
marker.setMinuteInterval(interval: 5)
marker.chartView = lineChartView
lineChartView.marker = marker
lineChartView.xAxis.setLabelCount(4, force: true)
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.xAxis.labelPosition = .bottom
lineChartView.leftAxis.labelPosition = .insideChart
lineChartView.rightAxis.drawLabelsEnabled = false
lineChartView.legend.enabled = false
lineChartView.data = data
lineChartView.data?.highlightEnabled = true
Update 2
To reproduce, I have the following class which creates the data (btw, it is a stub class, eventually it will contain a paid api, so for now I generate the data for test purposes:
class TideHeights {
var tideHeights = [Double]()
var tideTimes = [Date]()
var strTimes = [String]()
var intervalMins = 5
init (intervalMins: Int) {
self.intervalMins = intervalMins
let slots = 24 * 60.0 / Double(intervalMins)
let seconds = 24 * 60 * 60.0 / slots
let radians = 2 * Double.pi / slots
let endDate = Date().endOfDay
var date = Date().startOfDay
var radianOffset = 0.0
repeat {
tideHeights.append(sin(radianOffset) + 1)
tideTimes.append(date)
let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm"
strTimes.append(timeFormatter.string(from: date))
date = date.advanced(by: seconds)
radianOffset += radians
} while date <= endDate
}
}
this class is instantiated and used to draw the graph in the following two functions in ViewController.
func drawChart() -> Void {
let tideData = TideHeights(intervalMins: 5)
lineChartView.dragEnabled = true
lineChartView.setScaleEnabled(false)
lineChartView.pinchZoomEnabled = false
setChartValues(dataPoints: tideData.strTimes, values: tideData.tideHeights)
}
func setChartValues(dataPoints: [String], values: [Double]) -> Void {
//initialise and load array of chart data entries for drawing
var dataEntries: [ChartDataEntry] = []
for i in 0..<values.count {
let dataEntry = ChartDataEntry(x: Double(i), y: values[i])
dataEntries.append(dataEntry)
}
let tideHeights = LineChartDataSet(entries: dataEntries)
tideHeights.drawCirclesEnabled = false
tideHeights.colors = [NSUIColor.black]
let gradientColors = [ChartColorTemplates.colorFromString("#72E700").cgColor,
ChartColorTemplates.colorFromString("#724BEB").cgColor]
let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: nil)!
tideHeights.fillAlpha = 0.7
tideHeights.fill = Fill(linearGradient: gradient, angle: 90)
tideHeights.drawFilledEnabled = true
let data = LineChartData(dataSet: tideHeights)
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
let marker = ChartMarker()
marker.setMinuteInterval(interval: 5)
marker.chartView = lineChartView
lineChartView.marker = marker
lineChartView.xAxis.setLabelCount(4, force: true)
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.xAxis.labelPosition = .bottom
lineChartView.leftAxis.labelPosition = .insideChart
lineChartView.rightAxis.drawLabelsEnabled = false
lineChartView.legend.enabled = false
lineChartView.data = data
lineChartView.data?.highlightEnabled = true
}
The documentation says:
setLabelCount(int count, boolean force): Sets the number of labels for
the y-axis. Be aware that this number is not fixed (if force == false)
and can only be approximated. If force is enabled (true), then the
exact specified label-count is drawn – this can lead to uneven numbers
on the axis.
see https://weeklycoding.com/mpandroidchart-documentation/axis-general/
Remark: although the y-axis is mentioned in the documentation of the function, it can actually be applied to both axes.
So instead of
lineChartView.xAxis.labelCount = 7
use
lineChartView.xAxis.setLabelCount(9, force: true)
Note: you need 9 label counts not 7.
Test
If one combines a sine wave of 288 points with your code above
for i in 0...288 {
let val = sin(Double(i) * 2.0 * Double.pi / 288.0) + 1
dataEntries.append(ChartDataEntry(x: Double(i), y: val))
}
and forces the label count = 9 as shown, then it looks like this:
Stephan was correct in his answer to this question. To force gridlines and alignment, use setLabelCount(x, force: true) the additional complexity related to my specific project and the data contained in the labels.

pointSize property in SceneKit being Ignored

I'm trying to render a point cloud using SceneKit (ultimately for ARKit). However, I'm finding the pointSize (and minimumPointScreenSpaceRadius) properties are ignored when trying to modify the SCNGeometryElement. There was this solution to call on the underlying OpenGL property, but this seems to work only in simulation.
No matter what pointSize I put in, the points are the same size. This is roughly how my code is laid out. I'm wondering if I'm doing something wrong with my GeometryElement setup?
var index: Int = 0
for x_pos in stride(from: start , to: end, by: increment) {
for y_pos in stride(from: start , to: end, by: increment) {
for z_pos in stride(from: start , to: end, by: increment) {
let pos:SCNVector3 = SCNVector3(x: x_pos, y: y_pos, z: z_pos)
positions.append(pos)
normals.append(normal)
indices.append(index)
index = index + 1
}
}
}
let pt_positions : SCNGeometrySource = SCNGeometrySource.init(vertices: positions)
let n_positions: SCNGeometrySource = SCNGeometrySource.init(normals: normals)
let pointer = UnsafeRawPointer(indices)
let indexData = NSData(bytes: pointer, length: MemoryLayout<Int32>.size * indices.count)
let elements = SCNGeometryElement(data: indexData as Data, primitiveType: .point, primitiveCount: positions.count, bytesPerIndex: MemoryLayout<Int>.size)
elements.pointSize = 10.0 //being ignored
elements.minimumPointScreenSpaceRadius = 10 //also being ignored
let pointCloud = SCNGeometry (sources: [pt_positions, n_positions], elements: [elements])
pointCloud.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant
let ptNode = SCNNode(geometry: pointCloud)
scene.rootNode.addChildNode(ptNode)
you should also specify both element.minimumPointScreenSpaceRadius and element.maximumPointScreenSpaceRadius. if so your point size work! :)

Detect overlaping if enumerating nodes

I would like to know how should I detect overlapping nodes while enumerating them? Or how should I make that every random generated position in Y axis is at least some points higher or lower.
This is what I do:
1 - Generate random number between -400 and 400
2 - Add those into array
3 - Enumerate and add nodes to scene with generated positions like this:
var leftPositions = [CGPoint]()
for _ in 0..<randRange(lower: 1, upper: 5){
leftPositions.append(CGPoint(x: -295, y: Helper().randomBetweenTwoNumbers(firstNumber: leftSparkMinimumY, secondNumber: leftSparkMaximumY)))
}
leftPositions.enumerated().forEach { (index, point) in
let leftSparkNode = SKNode()
leftSparkNode.position = point
leftSparkNode.name = "LeftSparks"
let leftSparkTexture = SKTexture(imageNamed: "LeftSpark")
LeftSpark = SKSpriteNode(texture: leftSparkTexture)
LeftSpark.name = "LeftSparks"
LeftSpark.physicsBody = SKPhysicsBody(texture: leftSparkTexture, size: LeftSpark.size)
LeftSpark.physicsBody?.categoryBitMask = PhysicsCatagory.LeftSpark
LeftSpark.physicsBody?.collisionBitMask = PhysicsCatagory.Bird
LeftSpark.physicsBody?.contactTestBitMask = PhysicsCatagory.Bird
LeftSpark.physicsBody?.isDynamic = false
LeftSpark.physicsBody?.affectedByGravity = false
leftSparkNode.addChild(LeftSpark)
addChild(leftSparkNode)
}
But like this sometimes they overlap each other because the generated CGPoint is too close to the previous one.
I am trying to add some amount of triangles to the wall and those triangles are rotated by 90°
To describe in image what I want to achieve:
And I want to avoid thing like this:
Your approach to this is not the best, i would suggest only storing the Y values in your position array and check against those values to make sure your nodes will not overlap. The following will insure no two sparks are within 100 points of each other. You may want to change that value depending on your node's actual height or use case.
Now, obviously if you end up adding too many sparks within an 800 point range, this just will not work and cause an endless loop.
var leftPositions = [Int]()
var yWouldOverlap = false
for _ in 0..<randRange(lower: 1, upper: 5){
//Moved the random number generator to a function
var newY = Int(randY())
//Start a loop based on the yWouldOverlap Bool
repeat{
yWouldOverlap = false
//Nested loop to range from +- 100 from the randomly generated Y
for p in newY - 100...newY + 100{
//If array already contains one of those values
if leftPosition.contains(p){
//Set the loop Bool to true, get a new random value, and break the nested for.
yWouldOverlap = true
newY = Int(randY())
break
}
}
}while(yWouldOverlap)
//If we're here, the array does not contain the new value +- 100, so add it and move on.
leftPositions.append(newY)
}
func randY() -> CGFloat{
return Helper().randomBetweenTwoNumbers(firstNumber: leftSparkMinimumY, secondNumber: leftSparkMaximumY)
}
And here is a different version of your following code.
for (index,y) in leftPositions.enumerated() {
let leftSparkNode = SKNode()
leftSparkNode.position = CGPoint(x:-295,y:CGFloat(y))
leftSparkNode.name = "LeftSparks\(index)" //All node names should be unique
let leftSparkTexture = SKTexture(imageNamed: "LeftSpark")
LeftSpark = SKSpriteNode(texture: leftSparkTexture)
LeftSpark.name = "LeftSparks"
LeftSpark.physicsBody = SKPhysicsBody(texture: leftSparkTexture, size: LeftSpark.size)
LeftSpark.physicsBody?.categoryBitMask = PhysicsCatagory.LeftSpark
LeftSpark.physicsBody?.collisionBitMask = PhysicsCatagory.Bird
LeftSpark.physicsBody?.contactTestBitMask = PhysicsCatagory.Bird
LeftSpark.physicsBody?.isDynamic = false
LeftSpark.physicsBody?.affectedByGravity = false
leftSparkNode.addChild(LeftSpark)
addChild(leftSparkNode)
}

EXC_BAD_INSTRUCTION when spawning an object

So I'm trying to spawn a falling object in my game and i'm using an array to set all the possible xSpawn points and then I randomise through the array to get an x value but the problem is when It gets to this line in the code:
let SpawnPoint = UInt32(randomX)
It gives me the EXC_BAD_INSTRUCTION error and I can't seem to see why. I'm still new to swift so an explanation as to why it gives me an error at this point would be greatly cherished.
Full Code:
func spawnFallingOjects() {
let xSpawnOptions = [-50, -100, 0, 100, 150]
let randomX = xSpawnOptions[Int(arc4random_uniform(UInt32(xSpawnOptions.count)))]
let Bomb = SKSpriteNode(imageNamed: "YellowFrog")
Bomb.zPosition = 900
let SpawnPoint = UInt32(randomX)
Bomb.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height)
let action = SKAction.moveToY(-350, duration: 2.0)
Bomb.runAction(SKAction.repeatActionForever(action))
self.addChild(Bomb)
}
Two out of five members of xSpawnOptions are negative numbers, which cannot be represented in an unsigned integer. So when you try to convert them to such with:
let SpawnPoint = UInt32(randomX)
it crashes as you'd expect.
It's not clear what you're trying to do with your random number generation, but one way or another you need to change the logic of it to account for this, perhaps by calculating a random number which is always positive, and then adding or subtracting an offset to it, for example:
let SpawnPoint: UInt32 = 50
Bomb.position = CGPoint(x: CGFloat(Int(arc4random_uniform(SpawnPoint)) + randomX), y: self.size.height)