How to break up expressions involve bitwise shift operators? Swift - ios

The code below is generating an error and fails to run as the expression was too 'complex'.
for row in 0...NumRows {
for column in 0...NumColumns {
let topLeft = (column > 0) && (row < NumRows)
&& level.tileAt(column: column - 1, row: row) != nil
let bottomLeft = (column > 0) && (row > 0)
&& level.tileAt(column: column - 1, row: row - 1) != nil
let topRight = (column < NumColumns) && (row < NumRows)
&& level.tileAt(column: column, row: row) != nil
let bottomRight = (column < NumColumns) && (row > 0)
&& level.tileAt(column: column, row: row - 1) != nil
let value = Int(topLeft.hashValue) | Int(topRight.hashValue) << 1 | Int(bottomLeft.hashValue) << 2 | Int(bottomRight.hashValue) << 3
if value != 0 && value != 6 && value != 9 {
let name = String(format: "Tile_%ld", value)
let tileNode = SKSpriteNode(imageNamed: name)
tileNode.size = CGSize(width: TileWidth, height: TileHeight)
var point = pointFor(column: column, row: row)
point.x -= TileWidth/2
point.y -= TileHeight/2
tileNode.position = point
tilesLayer.addChild(tileNode)
}
}
}
Specifically this line:
let value = Int(topLeft.hashValue) | Int(topRight.hashValue) << 1 | Int(bottomLeft.hashValue) << 2 | Int(bottomRight.hashValue) << 3
The error is as follows:
Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions
How would you break up the expression for value in such a way that the code following it keeps the same answer?

hashValue is already an Int, so the Int(...) constructors are not
needed. This compiles in Xcode 9:
let value = topLeft.hashValue | (topRight.hashValue << 1) | (bottomLeft.hashValue << 2) | (bottomRight.hashValue << 3)
But that is a bad idea! It relies on the hash value of booleans having specific values:
false.hashValue == 0
true.hashValue == 1
which is nowhere guaranteed. (The only guarantee is that identical elements have the same hash value. One must not even assume that hash
values are equal across different executions of your program,
see Hashable).
A better solution would be
let value = (topLeft ? 1 : 0) + (topRight ? 2 : 0) + (bottomLeft ? 4 : 0) + (bottomRight ? 8 : 0)
Alternatively, use a switch-statement. Example:
switch (topLeft, topRight, bottomLeft, bottomRight) {
case (false, false, false, false):
// ... all false ...
case (false, true, true, false):
// ... topRight and bottomLeft are true ...
case (true, false, false, true):
// ... topLeft and bottomRight are true ...
default:
// ... all other cases ...
}

Related

Hackerrank New Year Chaos Swift

I am trying to solve Hackerrank's New Year Chaos problem in Swift. https://www.hackerrank.com/challenges/new-year-chaos/problem
It is about finding the number of bribes people made on a line waiting for a roller coaster ride. For example, there is a total of 3 bribes in this list [2, 1, 5, 3, 4].
Person 2 bribed person 1.
Person 5 bribed person 3 and 4.
If there are more than 2 bribes by a person, the line becomes "Too chaotic".
I was able to get an exponential solution. However, I want to make it linear.
func minimumBribes(q: [Int]) -> Void {
var bribeCount = 0
var chaotic = false
// for i in 0..<q.count {
// if q[i] - (i + 1) > 2 {
// chaotic = true
// break
// }
//
// for j in i + 1..<q.count {
// if q[i] > q[j] {
// bribeCount += 1
// }
// }
// }
var i = 0
while i < q.count - 1 {
if q[i] - (i + 1) > 2 {
chaotic = true
break
} else if q[i] > i + 1 {
bribeCount += (q[i] - (i + 1))
i += 1
} else if q[i] <= i + 1 && q[i] > q[i + 1] && q.indices.contains(i + 1) {
bribeCount += 1
i += 1
} else { // q[i] < q[i + 1]
i += 1
}
}
if chaotic {
print("Too chaotic")
} else {
print(bribeCount)
}
}
I commented out the exponential solution, which works. But the linear solution does not work and I cannot find out why. It works with the following arrays, [3,2,1,6,5,4], [2,5,1,3,4], [1,2,5,3,7,8,6,4], [1,3,4,2,7,6,5,9,8,11,10,14,13,12].
But there is a really long array in one of the test cases of the problem, which I do not get the correct answer with my linear solution.
For this long array, I get 966 with my exponential solution but the linear solution prints 905.
[2,1,5,6,3,4,9,8,11,7,10,14,13,12,17,16,15,19,18,22,20,24,23,21,27,28,25,26,30,29,33,32,31,35,36,34,39,38,37,42,40,44,41,43,47,46,48,45,50,52,49,51,54,56,55,53,59,58,57,61,63,60,65,64,67,68,62,69,66,72,70,74,73,71,77,75,79,78,81,82,80,76,85,84,83,86,89,90,88,87,92,91,95,94,93,98,97,100,96,102,99,104,101,105,103,108,106,109,107,112,111,110,113,116,114,118,119,117,115,122,121,120,124,123,127,125,126,130,129,128,131,133,135,136,132,134,139,140,138,137,143,141,144,146,145,142,148,150,147,149,153,152,155,151,157,154,158,159,156,161,160,164,165,163,167,166,162,170,171,172,168,169,175,173,174,177,176,180,181,178,179,183,182,184,187,188,185,190,189,186,191,194,192,196,197,195,199,193,198,202,200,204,205,203,207,206,201,210,209,211,208,214,215,216,212,218,217,220,213,222,219,224,221,223,227,226,225,230,231,229,228,234,235,233,237,232,239,236,241,238,240,243,242,246,245,248,249,250,247,244,253,252,251,256,255,258,254,257,259,261,262,263,265,264,260,268,266,267,271,270,273,269,274,272,275,278,276,279,277,282,283,280,281,286,284,288,287,290,289,285,293,291,292,296,294,298,297,299,295,302,301,304,303,306,300,305,309,308,307,312,311,314,315,313,310,316,319,318,321,320,317,324,325,322,323,328,327,330,326,332,331,329,335,334,333,336,338,337,341,340,339,344,343,342,347,345,349,346,351,350,348,353,355,352,357,358,354,356,359,361,360,364,362,366,365,363,368,370,367,371,372,369,374,373,376,375,378,379,377,382,381,383,380,386,387,384,385,390,388,392,391,389,393,396,397,394,398,395,401,400,403,402,399,405,407,406,409,408,411,410,404,413,412,415,417,416,414,420,419,422,421,418,424,426,423,425,428,427,431,430,429,434,435,436,437,432,433,440,438,439,443,441,445,442,447,444,448,446,449,452,451,450,455,453,454,457,456,460,459,458,463,462,464,461,467,465,466,470,469,472,468,474,471,475,473,477,476,480,479,478,483,482,485,481,487,484,489,490,491,488,492,486,494,495,496,498,493,500,499,497,502,504,501,503,507,506,505,509,511,508,513,510,512,514,516,518,519,515,521,522,520,524,517,523,525,526,529,527,531,528,533,532,534,530,537,536,539,535,541,538,540,543,544,542,547,548,545,549,546,552,550,551,554,553,557,555,556,560,559,558,563,562,564,561,567,568,566,565,569,572,571,570,575,574,577,576,579,573,580,578,583,581,584,582,587,586,585,590,589,588,593,594,592,595,591,598,599,596,597,602,603,604,605,600,601,608,609,607,611,612,606,610,615,616,614,613,619,618,617,622,620,624,621,626,625,623,628,627,631,630,633,629,635,632,637,636,634,638,640,642,639,641,645,644,647,643,646,650,648,652,653,654,649,651,656,658,657,655,661,659,660,663,664,666,662,668,667,670,665,671,673,669,672,676,677,674,679,675,680,678,681,684,682,686,685,683,689,690,688,687,693,692,691,696,695,698,694,700,701,702,697,704,699,706,703,705,709,707,711,712,710,708,713,716,715,714,718,720,721,719,723,717,722,726,725,724,729,728,727,730,733,732,735,734,736,731,738,737,741,739,740,744,743,742,747,746,745,750,748,752,749,753,751,756,754,758,755,757,761,760,759,764,763,762,767,765,768,766,771,770,769,774,773,776,772,778,777,779,775,781,780,783,784,782,786,788,789,787,790,785,793,791,792,796,795,794,798,797,801,799,803,800,805,802,804,808,806,807,811,809,810,814,812,813,817,816,819,818,815,820,821,823,822,824,826,827,825,828,831,829,830,834,833,836,832,837,839,838,841,835,840,844,842,846,845,843,849,847,851,850,852,848,855,854,853,857,856,858,861,862,860,859,863,866,865,864,867,870,869,868,872,874,875,871,873,877,878,876,880,881,879,884,883,885,882,888,886,890,891,889,893,887,895,892,896,898,894,899,897,902,901,903,905,900,904,908,907,910,909,906,912,911,915,913,916,918,914,919,921,917,923,920,924,922,927,925,929,928,926,932,931,934,930,933,935,937,939,940,938,936,943,944,942,941,947,946,948,945,951,950,949,953,952,956,954,958,957,955,961,962,963,959,964,966,960,965,969,968,971,967,970,974,972,976,973,975,979,977,981,982,978,980,983,986,984,985,989,988,987,990,993,991,995,994,997,992,999,1000,996,998]
Please help me figure out what is wrong with my solution. Thanks in advance!!
Here is my solution which passes all the test cases :)
func minimumBribes(q: [Int]) -> Void {
var bCount = 0
var isChaotic = false
for (key,value) in q.enumerated() {
if (value - 1) - key > 2 {
isChaotic = true
break
}
for index in stride(from: max(0, value - 2), to: key, by: 1){
if q[index] > value {
bCount += 1
}
}
}
isChaotic ? print("Too chaotic") : print("\(bCount)")
}
What you basically need to do is to first check if the element in each loop is on it's correct position. And if not you find out how much further is it from the right position if its greater than 2 you print "Too chaotic". Your solution is correct uptil this point. But if the difference is less than or equal to 2 then you need to increment the bribes and swap the indices to represent updated array. Furthermore if there are two swaps then you need to represent how the array would be effected by these 2 swaps and hence swap these values before the next iteration to ensure the array is in the condition it would be after these swaps.
Please refer to my solution below. It passes for all test cases:
func swapValues( arr:inout [Int],index:Int, times: Int, bribes:inout Int) -> Bool {
if times == 0 {
return false
}
if arr[index] > arr[index+1] {
let temp = arr[index+1]
arr[index+1] = arr[index]
arr[index] = temp
bribes = bribes + 1
return swapValues(arr: &arr, index: index+1, times: times-1,bribes: &bribes)
}else{
var diff = abs(arr[index+1] - (index+2))
if diff > 2 {
print("Too chaotic")
return true
}
var tooChaotic = swapValues(arr: &arr, index: index+1, times: diff,bribes:&bribes)
if tooChaotic {
return true
}
return swapValues(arr: &arr, index: index, times: times, bribes: &bribes)
}
}
func minimumBribes(q: [Int]) -> Void {
var qC = q
var bribes = 0
var i = 0
while i <= qC.count-1{
if i+1 == qC[i] {
i = i + 1
continue
}
let diff = abs(qC[i] - (i+1))
if diff > 2 {
print("Too chaotic")
return
}
var tooChaotic = swapValues(arr: &qC, index: i, times: diff, bribes: &bribes)
if tooChaotic {
return
}
}
print(bribes)
}
I found this short and easy solution.
func minimumBribes(q: [Int]) -> Void {
var ans = 0
var shouldShow = true
for i in stride(from: (q.count - 1), through: 0, by: -1) {
if (q[i] - (i+1) > 2) {
shouldShow = false
break;
}
for j in stride(from: max(0, q[i] - 2), to: i, by: 1){
if q[j] > q[i] {
ans += 1
}
}
}
if shouldShow {
print(ans)
} else {
print("Too chaotic")
} }
https://github.com/AnanthaKrish/example-ios-apps

index out of range array error when removing an element in swift

So I have a platform that spawns a mine and coins on it so first i spawn the mine using a bunch of spawn locations created basd on the size of the screen width and player like so:
var spawnLocations:[CGFloat] = []
func getObjectSpawnLocation() {
spawnLocations.removeAll()
//Create 5 possible spawn locations
let numberOfNodes = 5
// Spacing between nodes will change if: 1) number of nodes is changed, 2) screen width is changed, 3) node's size is changed.
for i in 0...numberOfNodes - 1 {
// spacing used to space out the nodes according to frame (which changes with screen width) (player width go up for closer nodes down for bigger space)
var xPosition = (frame.maxX + thePlayer.size.width / 0.57) / CGFloat((numberOfNodes - 1)) * CGFloat(i)
//add a half of a player's width because node's anchor point is (0.5, 0.5) by default
xPosition += thePlayer.size.width/2 //2
//I have no idea what this does but it works so fuck it.
xPosition -= frame.maxX/1 //1.6
spawnLocations.append( xPosition )
}
}
func spawnMine() {
if (theType == LevelType.normal && imageName == "WallObjectFarLeft") {
for _ in 0...0 {
getObjectSpawnLocation()
let obstacle:Object = Object()
obstacle.theType = LevelType.normal
obstacle.createObject()
obstacle.zPosition = 100
addChild(obstacle)
let randx = spawnLocations[0]
obstacle.position = CGPoint(x: randx, y: 0)
spawnLocations.remove(at: 0)
createSpecialObject()
}
}
then at the end of the spawn mine function I remove the mines location from the array so it can't be picked again and then I run the create special object function which creates the coin object on the platform like so:
func createSpecialObject() {
print("\(spawnLocations.count)")
for i in 0..<spawnLocations.count {
let diceRoll = arc4random_uniform(7) + 1
if (diceRoll <= 2) {
let specialObstacle:Object = Object()
specialObstacle.theType = LevelType.normal
specialObstacle.createSpecialObject()
specialObstacle.zPosition = 100
addChild(specialObstacle)
var randx = spawnLocations[i]
specialObstacle.position = CGPoint(x: randx, y: 0)
}
}
}
All this works fine when only removing one of the potential spawn locations from the array but when I try and remove two or even three locations from the array that have more than one mine on a platform and run the game it crashes and gives me a fatal error saying index out of range. What am I doing wrong here and how can I fix this? I remove them like so if there are more than one mine on a platform
else if (theType == LevelType.normal && imageName == "WallObjectFarLeft&Middle&FarRight") {
for num2 in 0 ..< 3 {
getObjectSpawnLocation()
let obstacle:Object = Object()
obstacle.theType = LevelType.normal
obstacle.createObject()
obstacle.zPosition = 100
addChild(obstacle)
var randx = spawnLocations[0]
if num2 == 1 {
randx = spawnLocations[2]
}
if num2 == 2 {
randx = spawnLocations[4]
}
obstacle.position = CGPoint(x: randx, y: 0)
}
spawnLocations.remove(at: 0)
spawnLocations.remove(at: 2)
spawnLocations.remove(at: 4)
createSpecialObject()
}
Here I am giving you example what actually what you'r trying to do in you below code work
spawnLocations.remove(at: 0)
spawnLocations.remove(at: 2)
spawnLocations.remove(at: 4)
In your project, your array have four values
Index - > 0, 1, 2, 3
Now, you remove object at index : 0, -> spawnLocations.remove(at: 0)
So now your updated array have 3 values,
index -> 0, 1, 2
In next step, you remove index : 2 -> spawnLocations.remove(at: 2)
So updated array will be,
index -> 0, 1
Now in the same array you have 2 values at index 0 & 1 and your are tring to remove object at index : 4 -> spawnLocations.remove(at: 4)
Hope you get your mistake.
Solution
Reverse you order of removing array object
spawnLocations.remove(at: 4)
spawnLocations.remove(at: 2)
spawnLocations.remove(at: 0)
init: [0,1,2,3,4]
remove at index 0
[1,2,3,4]
remove at index 2
[1,2,4]
remove at index 4
index out of range
You need to shift yours index after each remove
init: [0,1,2,3,4]
remove at index 0
[1,2,3,4]
remove at index 2 - 1
[1,3,4]
remove at index 4 - 2
[1,3]

How to make a general method to check for winners in Tic-Tac-Toe

I made a Tic-Tac-Toe game in Ruby. The method below checks for a winner in the vertical columns.
How do I make it so that this method can be applied to boards of different sizes, like 4x4, 6x6?
def vertical_check(array)
result = nil
if (array[0][0] == "X" && array[1][0] == "X" && array[2][0] == "X") ||
(array[0][1] == "X" && array[1][1] == "X" && array[2][1] == "X") ||
(array[0][2] == "X" && array[1][2] == "X" && array[2][2] == "X")
result = "X"
elsif (array[0][0] == "O" && array[1][0] == "O" && array[2][0] == "O") ||
(array[0][1] == "O" && array[1][1] == "O" && array[2][1] == "O") ||
(array[0][2] == "O" && array[1][2] == "O" && array[2][2] == "O")
result = "O"
else
result = nil
end
return result
end
The following is a failed attempt:
def vertical_check_x(array)
result = nil
index = 0
index2 = 0
until result != nil || index == array.length
while array[index][index2] == "X"
index += 1
end
if index == array.length
result = "X"
else
result = nil
index = array.length
end
index2 += 1
end
return result
end
def vertical_check_o(array)
result = nil
index = 0
index2 = 0
until result != nil || index == array.length
while array[index][index2] == "O"
index += 1
end
if index -1 == array.length
result = "O"
else
result = nil
index = array.length
end
index2 += 1
end
return result
end
def vertical_check(array)
result = vertical_check_x(array)
if result == nil
result = vertical_check_o(array)
end
return result
end
To quickly find a winner in given array, count the number of unique elements, confirm that there is only one unique element and if it is only X or O:
def winner arr
return arr[0] if arr.uniq.length == 1 && ['X', 'O'].include?(arr[0])
nil
end
The next problem is selecting the rows, columns and diagonals for an nxn array.
Rows are easy:
rows = arr.map {|row| row}
Columns are as follows - you select elements with the same index for each row:
cols = n.times.collect {|i| arr.map {|row| row[i]}}
Next are diagonals. There are two diagonals, one starts from leftmost corner and the other from the rightmost.
The leftmost diagonal has the sequence as:
(0, 0) -> (1, 1) -> (2, 2) ....
See the pattern?
diag = n.times.collect {|i| arr[i][i]}
The rightmost diagonal has pattern that goes like this (for a 3x3):
(0, 2) -> (1, 1) -> (2, 0)
For a 4x4, it's like this:
(0, 3) -> (1, 2) -> (2, 1) -> (3, 0)
So, the pattern for an nxn is:
(0, n-1-0) -> (1, n-1-1) -> (2, n-1-2) -> ... (i, n-1-i) ... -> (n-1, 0)
So:
diag = n.times.collect {|i| arr[i][n - 1 - i]}
Now, you can just do something like:
w = rows.map {|r| winner r}.compact[0]
for each array to get the winner.

Optimizing Performance for Level Generation

I'm generating simple game maps using a method described in this post. I'm using the second method of starting with a seed tile and randomly growing out. I'm testing it in a Swift playground on a 30x30 array. The problem is my method takes several minutes to generate a 30x30 map, and maps in my real game might exceed 100x100 tiles. How can I optimize the following code to run faster?
import UIKit
import SpriteKit
var map = [[String]](count: 30, repeatedValue:[String](count: 30, repeatedValue:" "))
var landTiles = [(y: Int,x: Int)]()
landTiles.append((15,x: 15))
let minX = 3
let maxX = 28
let minY = 3
let maxY = 28
var coastTiles = [(y: Int,x: Int)]()
var hasReachedBounds = false
while !hasReachedBounds {
coastTiles = []
for tile in landTiles {
let coastal = isCoastal(tile.y, x: tile.x)
if coastal {
coastTiles.append(tile)
}
}
let numTiles = UInt32(coastTiles.count)
print(map)
let randomTile = coastTiles[Int(arc4random_uniform(numTiles))]
if randomTile.x <= minX || randomTile.x >= maxX || randomTile.y <= minY || randomTile.y >= maxY {
hasReachedBounds = true
break
}
let coastal = isCoastal(randomTile.y, x: randomTile.x)
if !coastal{
continue
}
let randomPosition = arc4random_uniform(4) + 1
switch randomPosition {
case 1:
landTiles.append((randomTile.y + 1, randomTile.x))
map [randomTile.y + 1][randomTile.x] = "&"
case 2:
landTiles.append((randomTile.y, randomTile.x + 1))
map [randomTile.y][randomTile.x + 1] = "&"
case 3:
landTiles.append((randomTile.y - 1, randomTile.x))
map [randomTile.y - 1][randomTile.x] = "&"
case 4:
landTiles.append((randomTile.y, randomTile.x - 1))
map [randomTile.y][randomTile.x - 1] = "&"
default: break
}
}
func isCoastal (y: Int, x: Int) -> Bool{
if map[y + 1][x] == " " || map[y][x + 1] == " " || map[y - 1][x] == " " || map[y][x - 1] == " "{
return true
} else {
return false
}
}
print(map)

Is it possible to declare mutable and immutable values/bindings simultaneously?

For example I want to declare
let len, (*mutable*) i =
if s.Length >= 2 && s.[0] = '0' && (s.[1] = 'x' || s.[1] = 'X') then
(s.Length - 2, 2)
else (s.Length, 0)
constant binding len and mutable i, is it possible ?
Added :
I will use references then
let len, i =
if s.Length >= 2 && s.[0] = '0' && (s.[1] = 'x' || s.[1] = 'X') then
(s.Length - 2, ref 2)
else (s.Length, ref 0)
No. mutable applies to the entire let binding. You'll have to do:
let len, i = ...
let mutable i = i

Resources