I got error: '(Int, Int)' is not identical to 'CGPoint'
How to convert a (Int, Int) to CGPoint
let zigzag = [(100,100),
(100,150),(150,150),
(150,200)]
override func drawRect(rect: CGRect)
{
// Get the drawing context.
let context = UIGraphicsGetCurrentContext()
// Create the shape (a vertical line) in the context.
CGContextBeginPath(context)
//Error is here
CGContextAddLines(context, zigzag, zigzag.count)
// Configure the drawing environment.
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
// Request the system to draw.
CGContextStrokePath(context)
}
CGContextAddLines() expects an array of CGPoint. If you already have an
array of (Int, Int) tuples then you can convert it with
let points = zigzag.map { CGPoint(x: $0.0, y: $0.1) }
An alternate way to avoid the boilerplate code required to create instances of the same type is to make CGPoint implement the ArrayLiteralConvertible, making it initializable by assigning an array of CGFloat:
extension CGPoint : ArrayLiteralConvertible {
public init(arrayLiteral elements: CGFloat...) {
self.x = elements.count > 0 ? elements[0] : 0.0
self.y = elements.count > 1 ? elements[1] : 0.0
}
}
and then use it as follows:
let zigzag:[CGPoint] = [
[100,100],
[100,150],
[150,150],
[150,200]
]
A few notes:
stylistically, it doesn't look good - it would be good if literals could be used for tuples, but I am not aware of any way to do that
if an empty array is used, the CGPoint is initialized with x = 0 and y = 0
if an array with one element is used, it is initialized with y = 0
if more than 2 values are used, all the ones after the 2nd are ignored
If it tells you to use CGPoint, use it! Just (number,number) is a pair of ints.
let zigzag = [CGPointMake(100,100),
CGPointMake(100,150),CGPointMake(150,150),
CGPointMake(150,200)]
Yet another:
func CGPoints(points:(x:CGFloat, y:CGFloat)...) -> [CGPoint] {
return map(points) { CGPoint($0) }
}
let zigzag = CGPoints(
(100,100),(100,150),(150,150),(150,200)
)
Related
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.
This question already has answers here:
How can I handle different types using generic type in swift?
(2 answers)
Closed 6 years ago.
I'm trying to write a basic interpolation function in swift3. I get a lot of errors, though. This is obviously not the right way to use generics, but maybe I have a fundamental misunderstanding of their application?
class func interpolate<T>(from: T, to: T, progress: CGFloat) -> T
{
// Safety
assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)")
if let from = from as? CGFloat, let to = to as? CGFloat
{
return from + (to - from) * progress // No + candidates produce the expected contextual result type 'T'
}
if let from = from as? CGPoint, let to = to as? CGPoint
{
var returnPoint = CGPoint()
returnPoint.x = from.x + (to.x-from.x) * progress
returnPoint.y = from.y + (to.y-from.y) * progress
return returnPoint // Cannot convert return expression of type 'CGPoint' to return type 'T'
}
if let from = from as? CGRect, let to = to as? CGRect
{
var returnRect = CGRect()
returnRect.origin.x = from.origin.x + (to.origin.x-from.origin.x) * progress
returnRect.origin.y = from.origin.y + (to.origin.y-from.origin.y) * progress
returnRect.size.width = from.size.width + (to.size.width-from.size.width) * progress
returnRect.size.height = from.size.height + (to.size.height-from.size.height) * progress
return returnRect // Cannot convert return expression of type 'CGRect' to return type 'T'
}
return nil // Nil is incompatible with return type 'T'
}
A generic function is useful when you have the same operations to perform on several different types. That's basically what you have here. The problem is that you don't have the operations defined for two of the types that you care about, namely CGPoint and CGRect.
If you create separate functions to add, subtract, and multiply those types, you can make this generic function work. It would be simplified to
class func interpolate<T>(from: T, to: T, progress: CGFloat) -> T
{
// Safety
assert(0.0...1.0 ~= progress, "Invalid progress value: \(progress)")
return from + (to - from) * progress
}
I have code which is basically like this:
func arrayHalvesEqual(data:[UInt8]) -> Bool {
let midPoint = data.count / 2
for i in 0..<midPoint {
let b = data[i]
let b2 = data[i + midPoint]
if b != b2 {
return false
}
}
return true
}
This works fine, but sometimes I want to pass in Arrays, and other times ArraySlice. I thought I'd change it to use generics and the CollectionType protocol, which converts as follows:
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Generator.Element == UInt8>(data:ByteArray) -> Bool {
let midPoint = data.count / 2
for i in 0..<midPoint {
let b = data[i]
let b2 = data[i + midPoint]
if b != b2 {
return false
}
}
return true
}
However, I get the following compiler error:
error: binary operator '..<' cannot be applied to operands of type 'Int' and 'ByteArray.Index.Distance'
for i in 0..<midPoint {
I can switch the for loop to for i in data.indices which makes that compile, but then I can no longer divide it by 2 to get the midPoint, as data.indices returns the abstract CollectionType.Index whereas / 2 is an Int.
Is it possible to do something like this in Swift? Can I bridge between an abstract protocol Index type and some real type I can do maths on?
P.S: I've seen and found other examples for iterating over the whole collection by using indices and enumerate, but I explicitly only want to iterate over half the collection which requires some sort of division by 2
Thanks
You can restrict the method to collections which are indexed
by Int:
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Index == Int, ByteArray.Generator.Element == UInt8>
(data:ByteArray) -> Bool { ... }
This covers both Array and ArraySlice.
And if you use indices.startIndex instead of 0 as initial index
then it suffices to restrict the index type to IntegerType.
Also the data type UInt8 can be replaced by a generic Equatable,
and the entire method shortened to
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Index : IntegerType, ByteArray.SubSequence.Generator.Element : Equatable>
(data:ByteArray) -> Bool {
let midPoint = (data.indices.endIndex - data.indices.startIndex)/2
let firstHalf = data[data.indices.startIndex ..< midPoint]
let secondHalf = data[midPoint ..< data.indices.endIndex]
return !zip(firstHalf, secondHalf).contains { $0 != $1 }
}
How can I sort a dictionary in Swift?
My Dictionary declaration:
var colorDictionary = Dictionary<Pixel, Int>() //pixel class stores RGB values of a pixel, Int stores the appearing times of the same color showing on the same image.
My target:
I need the elements in the dictionary sorted by value (appearing times of colors) from high to low.
What I tried:
I have done some research online, and I know the Dictionary in swift doesn't provide the sort function. So I wrote the following code:
var tempArray = Array(colorDictionary.keys)
var sortedKeys: () = sort(&tempArray){
var obj1 = self.colorDictionary[$0]
var obj2 = self.colorDictionary[$1]
return obj1>obj2
}
println("the most color: \(colorDictionary[tempArray[0])")
Output I got: "the most color: Optional(27)" //where 27 is the highest appearing time of the color, which is the value of the dictionary.
Question: How could I make it return the key as well?
My Pixel Class:
//for making the hashvalue stuff equatable
func ==(lhs: Pixel, rhs: Pixel) -> Bool {
return lhs.hashValue == rhs.hashValue
}
//customized class for storing pixel info of an image
class Pixel: Hashable {
//RGB components from one pixel
let r: CGFloat
let g: CGFloat
let b: CGFloat
var y: CGFloat = 0
var u: CGFloat = 0
var v: CGFloat = 0
var theUIColorOfThisPixel:UIColor
//adding for make the key hashable
var hashValue: Int {
return theUIColorOfThisPixel.hashValue
}
init(_thePixel: UIColor){
r = _thePixel.components.red
g = _thePixel.components.green
b = _thePixel.components.blue
theUIColorOfThisPixel=UIColor(red: r, green: g, blue: b, alpha: 1)
rgbToYuv(r, _g: g, _b: b)
}
}
[Problem solved]
My solution:
if I convert the result to Int (e.g. Int(colorDictionary[tempArray[0]]), it will just return the appearing time of the most common color on the image. For getting the UIColor of the pixel, I used:
var theMostUIColor: UIColor = tempArray[0].theUIColorOfThisPixel
I thought after storing my dictionary to the Array, it will just store the values. But now I found it actually stores the keys as well. Thanks for all the people who replied on this post. I appreciate!
You can return an array of tuples. Where each tuple has two values; the Pixel and the integer.
See: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID164
So, I'm trying to develop a simple game written in Swift, but I'm having trouble doing a pretty simple thing. I can't manage to create a random CGPoint... When using arc4random, a compiler error shows up telling me that I can't use Int32 in a CGPoint. So, Is there any way to do this? Any workaround? Thanks!
can also maybe make use of Swift's extensions of base types to create a reusable set of overloaded functions of CGPoint. Maybe something like:
extension CGPoint {
func random()->CGPoint { return CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))}
func random(range:Int)->CGPoint {
return CGPoint(x:Int(arc4random()%range),y:Int(arc4random()%range))}
func random(rangeX:Int, rangeY:Int)->CGPoint {
return CGPoint(x:Int(arc4random()%rangeX),y:Int(arc4random()%rangeY))}
}
You can then write random CGPoints like this:
var p = CGPoint.random()
//random x and y with a range of 1000
or
var p = CGPoint.random(range:100)
//random x and y with a range of 100
or
var p = CGPoint.random(rangeX:200, rangeY:400)
//random x up to 200 and random y with a range of up to 400
Granted, I'm not in the Xcode IDE at the moment to check syntax / if it compiles correctly but hope that could be of help :-)
...
//////////////////
Swift 1.2 Update
//////////////////
Seems these type-level function calls are not allowed anymore with extensions...at least for CGPoint; probably because CGPoint is actually a struct and not a class based on the current IOS documentation.
Here's a more in-depth version of my extension that allows for Range types.
This is confirmed working as of XCode 6.4 Beta
(Github repository with Playground file found here:
https://github.com/princetrunks/Random-CGPoint-Extension)
//creates random CGPoints in Swift as of XCode Beta 6.4 (6E7)
extension CGPoint {
/*private functions that help alleviate the ambiguity of the modulo bias
and nested typecasting as well as recycle similar functionality
for either Int or Range type parameter inputs */
private func randomInt(num:Int) ->Int{
return Int(arc4random_uniform(UInt32(num)))
}
private func randomIntFromRange(numRange:Range<Int>) ->Int{
return Int(arc4random_uniform(UInt32((numRange.endIndex - numRange.startIndex) + numRange.startIndex)))
}
//private variable for the default range
private var defaultRange : Int{
get{return 1000}
}
//(a) public variable that creates a default random CGPoint
static var randomPoint = CGPoint.zeroPoint.random()
//(b) default random point creation
func random()->CGPoint { return CGPoint(x:randomInt(defaultRange),y:randomInt(defaultRange))}
//(c) using an Int parameter for both the random x and y range
func random(range:Int)->CGPoint {
return CGPoint(x:randomInt(range),y:randomInt(range))
}
//(d) allows for the specification of the x and y random range
func random(#rangeX:Int, rangeY:Int)->CGPoint {
return CGPoint(x:randomInt(rangeX),y:randomInt(rangeY))
}
//(e) allows the same functionality as (c) but with a Range<Int> type parameter
func random(range:Range<Int>)->CGPoint {
return CGPoint(x:randomIntFromRange(range), y:randomIntFromRange(range))
}
//(f) allows the same functionality as (d) but with a Range<Int> type parameter
func random(#rangeX:Range<Int>, rangeY:Range<Int> )->CGPoint {
return CGPoint(x:randomIntFromRange(rangeX), y:randomIntFromRange(rangeY))
}
}
Here's how we can test this extension:
//(a)
let r = CGPoint.randomPoint
//(b)
var anotherRandomPoint = r.random()
//(c)
anotherRandomPoint = r.random(1000)
//(d)
anotherRandomPoint = r.random(0...1000)
//(e)
anotherRandomPoint = r.random(rangeX:90, rangeY: 2000)
//(f)
anotherRandomPoint = r.random(rangeX:0...90, rangeY: 0...2000)
// generates 100 random CGPoints between -1000 and 999
for _ in 0...100 {
anotherRandomPoint.random(-1000...1000)
}
hi what about constructing an Int? Int(arc4random())
e.g.
var p = CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))
Swift 4,5
// Add some range
let minX = 0
let maxX = 100
let minY = 0
let maxY = 100
let randomX = CGFloat.random(in: minX..<maxX)
let randomY = CGFloat.random(in: minY..<maxY)
let random = CGPoint(x: randomX, y: randomY)
Here is an extension on CGPoint to generate random point based on your x,y closed range.
extension CGPoint {
static func randPoint(xRange: ClosedRange<CGFloat>, yRange: ClosedRange<CGFloat>) -> Self {
let x = CGFloat.random(in: xRange)
let y = CGFloat.random(in: yRange)
return .init(x: x, y: y)
}
}