Adding Alpha-Beta prunning to minMax in Objective-C - objective-c

Hi there I have problems adding alpha beta pruning to my minMax algorithm for the connect 4 game can you help me ? here is my code for the minMax procedure, it looks to me that there is just a little that have to be done what eludes me :S scoreBoard() is my Heuristics function and I returning an array with 2 values the first one is the position of on the table and the other one is the score for this position.
-(NSArray*) miniMaxWihtAlphaBetaPrunning:(BOOL)maxOrMin withAlpha:(NSInteger)alpha
withBeta:(NSInteger)beta withPlayer:(enum playerColor)player andTreeDepth:
(NSInteger)depth
{
if (depth == 0)
{
return [NSArray arrayWithObjects:[NSNumber numberWithInt:-1],
[NSNumber numberWithInt:scoreBoard()],nil];
}
else
{
NSInteger bestScore = maxOrMin ? redWins: blueWins;
NSInteger bestMove = -1;
for (NSInteger column = 0; column < 10; column++)
{
if (discPlacedMatrix[0][column] != 0)
{
continue;
}
NSInteger rowFilled = dropDiscAtPoint(column, player);
if (rowFilled == -1)
{
continue;
}
NSInteger s = scoreBoard();
if (s == (maxOrMin? blueWins : redWins))
{
bestMove = column;
bestScore = s;
discPlacedMatrix[rowFilled][column] = 0;
break;
}
NSArray* result = [NSArray arrayWithArray:[self miniMaxWihtAlphaBetaPrunning:!maxOrMin withAlpha:alpha withBeta:beta withPlayer:(player == 1 ? RED : BLUE) andTreeDepth:depth - 1]];
NSInteger scoreInner = [[result objectAtIndex:1] intValue];
discPlacedMatrix[rowFilled][column] = 0;
if (scoreInner == blueWins || scoreInner == redWins)
{
scoreInner -= depth * player;
}
if (maxOrMin)
{
if (scoreInner >= bestScore)
{
bestScore = scoreInner;
bestMove = column;
}
}
else
{
if (scoreInner <= bestScore)
{
bestScore = scoreInner;
bestMove = column;
}
}
}
return [NSArray arrayWithObjects:[NSNumber numberWithInt:bestMove],[NSNumber numberWithInt:bestScore],nil];
}
}
I tried some scenarios but the Ai started to

It sounds like you're looking for something like the jamboree algorithm. The basic idea of this algorithm is to go through your tree recursively, and at each level, run the alpha beta algorithm on part of the nodes at that level, and then run the mini-max algorithm on the rest of the nodes. I'm at work right now, but I'll elaborate when I get home.
An implementation:
http://chessprogramming.wikispaces.com/Jamboree

Related

Why is my collision prediction not detecting any collisions?

Bullet objects are created with .l for location and .vel for velocity. I'm trying with a large radius of 30 and still they never collide.
- (void)bulletsFired:(NSArray *)bullets li:(int)li {
[self playBulletSound:li];
for (Bullet *b in bullets) {
[self addChild:b];
b.tag = -1;
b.shotNumber = shotsFired;
}
for (Bullet *b in bullets) {
for (Bullet *bb in self.bullets) {
float timeOfApproach = TimeOfClosestApproach(b.l, b.vel, bb.l, bb.vel, 30, 30);
if (timeOfApproach > 0) {
NSLog(#"time of : %f", timeOfApproach);
NSString *keyName = [self.collisions objectForKey:[self keyNameForTime:(int)timeOfApproach]];
NSMutableArray *timedCollisions = [self.collisions objectForKey:keyName];
if (timedCollisions == nil) {
NSMutableArray *newCollisions = [NSMutableArray array];
[self.collisions setObject:newCollisions forKey:keyName];
}
NSDictionary *collision = #{#"b1" : [NSString stringWithFormat:#"%d", bb.shotNumber], #"b2" : [NSString stringWithFormat:#"%d", b.shotNumber]};
[timedCollisions addObject:collision];
}
}
}
[self.bullets addObjectsFromArray:bullets];
[self.scoreCycler score:1];
}
I check for timeOfClosestApproach with this function:
float TimeOfClosestApproach(CGPoint Pa, CGPoint Pb, CGPoint Va, CGPoint Vb, float Ra, float Rb) {
CGPoint Pab = ccpSub(Pa, Pb);
CGPoint Vab = ccpSub(Va, Vb);
float a = ccpDot(Vab, Vab);
float b = 2 * ccpDot(Pab, Vab);
float c = ccpDot(Pab, Pab) - (Ra + Rb) * (Ra + Rb);
// The quadratic discriminant.
float discriminant = b * b - 4 * a * c;
// Case 1:
// If the discriminant is negative, then there are no real roots, so there is no collision. The time of
// closest approach is then given by the average of the imaginary roots, which is: t = -b / 2a
float t;
if (discriminant < 0) {
t = -b / (2 * a);
return -1;
} else {
// Case 2 and 3:
// If the discriminant is zero, then there is exactly one real root, meaning that the circles just grazed each other. If the
// discriminant is positive, then there are two real roots, meaning that the circles penetrate each other. In that case, the
// smallest of the two roots is the initial time of impact. We handle these two cases identically.
float t0 = (-b + (float)sqrt(discriminant)) / (2 * a);
float t1 = (-b - (float)sqrt(discriminant)) / (2 * a);
t = min(t0, t1);
// We also have to check if the time to impact is negative. If it is negative, then that means that the collision
// occured in the past. Since we're only concerned about future events, we say that no collision occurs if t < 0.
if (t < 0) {
return -1;
} else {
}
}
// Finally, if the time is negative, then set it to zero, because, again, we want this function to respond only to future events.
if (t < 0) {
t = 0;
}
return t;
}
I keep getting -1 returned, and the bullets are never predicted to collide, even when they should based on my eyesight.
if (t < 0) {
return -1;
} else {
}
Keeps getting triggered.
What's wrong with my timeOfClosestApproach function?

`resolve_dtor() `Parentheses Algorithm

I'm running into a frustrating problem with a parentheses completion algorithm. The math library I'm using, DDMathParser, only processes trigonometric functions in radians. If one wishes to use degrees, they must call dtor(deg_value). The problem is that this adds an additional parenthesis that must be accounted for at the end.
For example, the math expression sin(cos(sin(x))) would translate to sin(dtor(cos(dtor(sin(dtor(x))) in my code. However, notice that I need two additional parentheses in order for it to be a complete math expression. Hence, the creation of resolve_dtor().
Here is my attempted solution, the idea was to have 0 signify a left-paren, 1 signify a left-paren with dtor(, and 2 signify a right-paren thus completing either 0 or 1.
- (NSMutableString *)resolve_dtor:(NSMutableString *)exp
{
NSInteger mutable_length = [exp length];
NSMutableArray *paren_complete = [[NSMutableArray alloc] init]; // NO/YES
for (NSInteger index = 0; index < mutable_length; index++) {
if ([exp characterAtIndex:index] == '(') {
// Check if it is "dtor()"
if (index > 5 && [[exp substringWithRange:NSMakeRange(index - 4, 4)] isEqual:#"dtor"]) {
//dtor_array_index = [self find_incomplete_paren:paren_complete];
[paren_complete addObject:#1];
}
else
[paren_complete addObject:#0]; // 0 signifies an incomplete parenthetical expression
}
else if ([exp characterAtIndex:index] == ')' && [paren_complete count] >= 1) {
// Check if "dtor("
if (![self elem_is_zero:paren_complete]) {
// Add right-paren for "dtor("
[paren_complete replaceObjectAtIndex:[self find_incomplete_dtor:paren_complete] withObject:#2];
[exp insertString:#")" atIndex:index + 1];
mutable_length++;
index++;
}
else
[paren_complete replaceObjectAtIndex:[self find_incomplete_paren:paren_complete] withObject:#2];
}
else if ([paren_complete count] >= 1 && [[paren_complete objectAtIndex:0] isEqualToValue:#2]) {
// We know that everything is complete
[paren_complete removeAllObjects];
}
}
return exp;
}
- (bool)check_dtor:(NSMutableString *)exp
{
NSMutableArray *paren_complete = [[NSMutableArray alloc] init]; // NO/YES
for (NSInteger index = 0; index < [exp length]; index++) {
if ([exp characterAtIndex:index] == '(') {
// Check if it is "dtor()"
if (index > 5 && [[exp substringWithRange:NSMakeRange(index - 4, 4)] isEqual:#"dtor"]) {
//dtor_array_index = [self find_incomplete_paren:paren_complete];
[paren_complete addObject:#1];
}
else
[paren_complete addObject:#0]; // 0 signifies an incomplete parenthetical expression
}
else if ([exp characterAtIndex:index] == ')' && [paren_complete count] >= 1) {
// Check if "dtor("
if (![self elem_is_zero:paren_complete]) {
// Indicate "dtor(" at index is now complete
[paren_complete replaceObjectAtIndex:[self find_incomplete_dtor:paren_complete] withObject:#2];
}
else
[paren_complete replaceObjectAtIndex:[self find_incomplete_paren:paren_complete] withObject:#2];
}
else if ([paren_complete count] >= 1 && [[paren_complete objectAtIndex:0] isEqualToValue:#2]) {
// We know that everything is complete
[paren_complete removeAllObjects];
}
}
// Now step back and see if all the "dtor(" expressions are complete
for (NSInteger index = 0; index < [paren_complete count]; index++) {
if ([[paren_complete objectAtIndex:index] isEqualToValue:#0] || [[paren_complete objectAtIndex:index] isEqualToValue:#1]) {
return NO;
}
}
return YES;
}
It seems the algorithm works for sin((3 + 3) + (6 - 3)) (translating to sin(dtor((3 + 3) x (6 - 3))) but not sin((3 + 3) + cos(3)) (translating to sin(dtor((3 + 3) + cos(dtor(3)).
Bottom Line
This semi-solution is most likely overcomplicated (one of my common problems, it seems), so I was wondering if there might be an easier way to do this?
Solution
Here is my solution to #j_random_hacker's pseudo code he provided:
- (NSMutableString *)resolve_dtor:(NSString *)exp
{
uint depth = 0;
NSMutableArray *stack = [[NSMutableArray alloc] init];
NSRegularExpression *regex_trig = [NSRegularExpression regularExpressionWithPattern:#"(sin|cos|tan|csc|sec|cot)" options:0 error:0];
NSRegularExpression *regex_trig2nd = [NSRegularExpression regularExpressionWithPattern:#"(asin|acos|atan|acsc|asec|acot)" options:0 error:0];
// need another regex for checking asin, etc. (because of differing index size)
NSMutableString *exp_copy = [NSMutableString stringWithString:exp];
for (NSInteger i = 0; i < [exp_copy length]; i++) {
// Check for it!
if ([exp_copy characterAtIndex:i] == '(') {
if (i >= 4) {
// check if i - 4
if ([regex_trig2nd numberOfMatchesInString:exp_copy options:0 range:NSMakeRange(i - 4, 4)] == 1) {
[stack addObject:#(depth)];
[exp_copy insertString:#"dtor(" atIndex:i + 1];
depth++;
}
}
else if (i >= 3) {
// check if i - 3
if ([regex_trig numberOfMatchesInString:exp_copy options:0 range:NSMakeRange(i - 3, 3)] == 1) {
[stack addObject:#(depth)];
[exp_copy insertString:#"dtor(" atIndex:i + 1];
depth++;
}
}
}
else if ([exp_copy characterAtIndex:i] == ')') {
depth--;
if ([stack count] > 0 && [[stack objectAtIndex:[stack count] - 1] isEqual: #(depth)]) {
[stack removeObjectAtIndex:[stack count] - 1];
[exp_copy insertString:#")" atIndex:i + 1];
}
}
}
return exp_copy;
}
It works! Let me know if there are any minor corrections that would be good to add or if there is a more efficient approach.
Haven't tried reading your code, but I would use a simple approach in which we scan forward through the input string writing out a second string as we go while maintaining a variable called depth that records the current nesting level of parentheses, as well as a stack that remembers the nesting levels that need an extra ) because we added a dtor( when we entered them:
Set depth to 0.
For each character c in the input string:
Write it to the output.
Is c a (? If so:
Was the preceding token sin, cos etc.? If so, push the current value of depth on a stack, and write out dtor(.
Increment depth.
Is c a )? If so:
Decrement depth.
Is the top of the stack equal to depth? If so, pop it and write out ).
DDMathParser natively supports using degrees for trigonometric functions and will insert the relevant dtor functions for you. It'll even automatically insert it by doing:
#"sin(42°)"
You can do this by setting the angleMeasurementMode on the relevant DDMathEvaluator object.

Project Euler # 10 in Objective C

I'm trying to solve Problem 10 in Project Euler, and while I thought I had it, its saying my answer is incorrect. The question is as follows:
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.
And my code:
int sum;
#interface Prime : NSObject
-(BOOL)isPrime:(int)arg1;
#end
#implementation Prime
-(BOOL)isPrime:(int)arg1 {
if (arg1 == 1) {
NSLog(#"Given 1");
return NO;
}
for (int i = 2; i < arg1; i++) {
if (arg1 % i == 0) {
return NO;
}
}
sum += arg1;
return YES;
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Prime* primeObject = [[Prime alloc] init];
for (int i = 0; i < 2000000; i++) {
[primeObject isPrime:i];
}
NSLog(#"Sum of primes is %i", sum);
}
}
This code outputs 'Sum of primes is 1179908154' which Project Euler says is incorrect. Help?
The problem is that the sum does not fit into a 32-bit integer. You should use long long instead.
Just a guess, you should try to:
Initialise the sum variable to 0.
Try not to use a global variable like sum that can be accessed from anywhere, in this case do the sum in the main loop instead of in the isPrime method.
Maybe that'll give you the right answer.
You are using int for getting result, so it is wrong.
I'm using long int instead, that is enough for this case.
Here is my code, and it works fine:
int inputNumber = 2000000;
long int result = 0;
for (int i = 2; i < inputNumber; i++) {
BOOL isPrime = YES;
for (int j = 2; j <= sqrt(i); j++) {
if (i%j==0) {
isPrime = NO;
break;
}
}
if (isPrime) {
result += i;
}
}
Result is: 142913828922

Efficient way of checking the content of every NSDictionary in NSArray

In my app I'me getting responses from the server and I have to check that I don't create duplicate objects in the NSArray which contains NSDictionaries. Now to check if the objects exists I do this:
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
}
if (i == appDelegate.currentUser.userSiteDetailsArray.count - 1 && ![[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier] && needToCheck){
// It means it's the last object we've iterated through and needToCheck is still = YES;
//Doing stuff here
}
}
I set up a BOOL value because this iteration goes numerous times inside a method and I can't use return to stop it. I think there is a better way to perform this check and I would like to hear your suggestions about it.
BOOL needToCheck = YES;
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
break;
}
}
if (needToCheck) {
//Doing stuff here
}
But, as others have said, you can maybe keep a "summary" in a separate NSSet that you check first, vs spinning through all the dictionaries.
NSDictionary *previousThing = nil;
for (NSDictionary *thing in appDelegate.currentUser.userSiteDetailsArray) {
if ([thing[#"webpropID"] isEqualToString:newWebPropertyIdentifier]) {
previousThing = thing;
break;
}
}
if (previousThing == nil) {
// no previous thing
} else {
// duplicate
}

Compare version numbers in Objective-C

I am writing an application that receives data with items and version numbers. The numbers are formatted like "1.0.1" or "1.2.5". How can I compare these version numbers? I think they have to be formatted as a string first, no? What options do I have to determine that "1.2.5" comes after "1.0.1"?
This is the simplest way to compare versions, keeping in mind that "1" < "1.0" < "1.0.0":
NSString* requiredVersion = #"1.2.0";
NSString* actualVersion = #"1.1.5";
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
// actualVersion is lower than the requiredVersion
}
I'll add my method, which compares strictly numeric versions (no a, b, RC etc.) with any number of components.
+ (NSComparisonResult)compareVersion:(NSString*)versionOne toVersion:(NSString*)versionTwo {
NSArray* versionOneComp = [versionOne componentsSeparatedByString:#"."];
NSArray* versionTwoComp = [versionTwo componentsSeparatedByString:#"."];
NSInteger pos = 0;
while ([versionOneComp count] > pos || [versionTwoComp count] > pos) {
NSInteger v1 = [versionOneComp count] > pos ? [[versionOneComp objectAtIndex:pos] integerValue] : 0;
NSInteger v2 = [versionTwoComp count] > pos ? [[versionTwoComp objectAtIndex:pos] integerValue] : 0;
if (v1 < v2) {
return NSOrderedAscending;
}
else if (v1 > v2) {
return NSOrderedDescending;
}
pos++;
}
return NSOrderedSame;
}
This is an expansion to Nathan de Vries answer to address the problem of 1 < 1.0 < 1.0.0 etc.
First off we can address the problem of extra ".0"'s on our version string with an NSString category:
#implementation NSString (VersionNumbers)
- (NSString *)shortenedVersionNumberString {
static NSString *const unnecessaryVersionSuffix = #".0";
NSString *shortenedVersionNumber = self;
while ([shortenedVersionNumber hasSuffix:unnecessaryVersionSuffix]) {
shortenedVersionNumber = [shortenedVersionNumber substringToIndex:shortenedVersionNumber.length - unnecessaryVersionSuffix.length];
}
return shortenedVersionNumber;
}
#end
With the above NSString category we can shorten our version numbers to drop the unnecessary .0's
NSString* requiredVersion = #"1.2.0";
NSString* actualVersion = #"1.1.5";
requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2
actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5
Now we can still use the beautifully simple approach proposed by Nathan de Vries:
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
// actualVersion is lower than the requiredVersion
}
I made it myself,use Category..
Source..
#implementation NSString (VersionComparison)
- (NSComparisonResult)compareVersion:(NSString *)version{
NSArray *version1 = [self componentsSeparatedByString:#"."];
NSArray *version2 = [version componentsSeparatedByString:#"."];
for(int i = 0 ; i < version1.count || i < version2.count; i++){
NSInteger value1 = 0;
NSInteger value2 = 0;
if(i < version1.count){
value1 = [version1[i] integerValue];
}
if(i < version2.count){
value2 = [version2[i] integerValue];
}
if(value1 == value2){
continue;
}else{
if(value1 > value2){
return NSOrderedDescending;
}else{
return NSOrderedAscending;
}
}
}
return NSOrderedSame;
}
Test..
NSString *version1 = #"3.3.1";
NSString *version2 = #"3.12.1";
NSComparisonResult result = [version1 compareVersion:version2];
switch (result) {
case NSOrderedAscending:
case NSOrderedDescending:
case NSOrderedSame:
break;
}
Sparkle (the most popular software update framework for MacOS) has a SUStandardVersionComparator class that does this, and also takes into account build numbers and beta markers. I.e. it correctly compares 1.0.5 > 1.0.5b7 or 2.0 (2345) > 2.0 (2100). The code only uses Foundation, so should work fine on iOS as well.
Check out my NSString category that implements easy version checking on github; https://github.com/stijnster/NSString-compareToVersion
[#"1.2.2.4" compareToVersion:#"1.2.2.5"];
This will return a NSComparisonResult which is more accurate then using;
[#"1.2.2" compare:#"1.2.2.5" options:NSNumericSearch]
Helpers are also added;
[#"1.2.2.4" isOlderThanVersion:#"1.2.2.5"];
[#"1.2.2.4" isNewerThanVersion:#"1.2.2.5"];
[#"1.2.2.4" isEqualToVersion:#"1.2.2.5"];
[#"1.2.2.4" isEqualOrOlderThanVersion:#"1.2.2.5"];
[#"1.2.2.4" isEqualOrNewerThanVersion:#"1.2.2.5"];
Swift 2.2 Version :
let currentStoreAppVersion = "1.10.2"
let minimumAppVersionRequired = "1.2.2"
if currentStoreAppVersion.compare(minimumAppVersionRequired, options: NSStringCompareOptions.NumericSearch) ==
NSComparisonResult.OrderedDescending {
print("Current Store version is higher")
} else {
print("Latest New version is higher")
}
Swift 3 Version :
let currentStoreVersion = "1.1.0.2"
let latestMinimumAppVersionRequired = "1.1.1"
if currentStoreVersion.compare(latestMinimumAppVersionRequired, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
print("Current version is higher")
} else {
print("Latest version is higher")
}
I thought I'd just share a function I pulled together for this. It is not perfect at all. Please take a look that the examples and results. But if you are checking your own version numbers (which I have to do to manage things like database migrations) then this may help a little.
(also, remove the log statements in the method, of course. those are there to help you see what it does is all)
Tests:
[self isVersion:#"1.0" higherThan:#"0.1"];
[self isVersion:#"1.0" higherThan:#"0.9.5"];
[self isVersion:#"1.0" higherThan:#"0.9.5.1"];
[self isVersion:#"1.0.1" higherThan:#"1.0"];
[self isVersion:#"1.0.0" higherThan:#"1.0.1"];
[self isVersion:#"1.0.0" higherThan:#"1.0.0"];
// alpha tests
[self isVersion:#"1.0b" higherThan:#"1.0a"];
[self isVersion:#"1.0a" higherThan:#"1.0b"];
[self isVersion:#"1.0a" higherThan:#"1.0a"];
[self isVersion:#"1.0" higherThan:#"1.0RC1"];
[self isVersion:#"1.0.1" higherThan:#"1.0RC1"];
Results:
1.0 > 0.1
1.0 > 0.9.5
1.0 > 0.9.5.1
1.0.1 > 1.0
1.0.0 < 1.0.1
1.0.0 == 1.0.0
1.0b > 1.0a
1.0a < 1.0b
1.0a == 1.0a
1.0 < 1.0RC1 <-- FAILURE
1.0.1 < 1.0RC1 <-- FAILURE
notice that alpha works but you have to be very careful with it. once you go alpha at some point you cannot extend that by changing any other minor numbers behind it.
Code:
- (BOOL) isVersion:(NSString *)thisVersionString higherThan:(NSString *)thatVersionString {
// LOWER
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending) {
NSLog(#"%# < %#", thisVersionString, thatVersionString);
return NO;
}
// EQUAL
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedSame) {
NSLog(#"%# == %#", thisVersionString, thatVersionString);
return NO;
}
NSLog(#"%# > %#", thisVersionString, thatVersionString);
// HIGHER
return YES;
}
My iOS library AppUpdateTracker contains an NSString category to perform this sort of comparison. (Implementation is based off DonnaLea's answer.)
Usage would be as follows:
[#"1.4" isGreaterThanVersionString:#"1.3"]; // YES
[#"1.4" isLessThanOrEqualToVersionString:#"1.3"]; // NO
Additionally, you can use it to keep track of your app's installation/update status:
[AppUpdateTracker registerForAppUpdatesWithBlock:^(NSString *previousVersion, NSString *currentVersion) {
NSLog(#"app updated from: %# to: %#", previousVersion, currentVersion);
}];
[AppUpdateTracker registerForFirstInstallWithBlock:^(NSTimeInterval installTimeSinceEpoch, NSUInteger installCount) {
NSLog(#"first install detected at: %f amount of times app was (re)installed: %lu", installTimeSinceEpoch, (unsigned long)installCount);
}];
[AppUpdateTracker registerForIncrementedUseCountWithBlock:^(NSUInteger useCount) {
NSLog(#"incremented use count to: %lu", (unsigned long)useCount);
}];
Here is the swift 4.0 + code for version comparison
let currentVersion = "1.2.0"
let oldVersion = "1.1.1"
if currentVersion.compare(oldVersion, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
print("Higher")
} else {
print("Lower")
}
Glibc has a function strverscmp and versionsort… unfortunately, not portable to the iPhone, but you can write your own fairly easily. This (untested) re-implementation comes from just reading the documented behavior, and not from reading Glibc's source code.
int strverscmp(const char *s1, const char *s2) {
const char *b1 = s1, *b2 = s2, *e1, *e2;
long n1, n2;
size_t z1, z2;
while (*b1 && *b1 == *b2) b1++, b2++;
if (!*b1 && !*b2) return 0;
e1 = b1, e2 = b2;
while (b1 > s1 && isdigit(b1[-1])) b1--;
while (b2 > s2 && isdigit(b2[-1])) b2--;
n1 = strtol(b1, &e1, 10);
n2 = strtol(b2, &e2, 10);
if (b1 == e1 || b2 == e2) return strcmp(s1, s2);
if (n1 < n2) return -1;
if (n1 > n2) return 1;
z1 = strspn(b1, "0"), z2 = strspn(b2, "0");
if (z1 > z2) return -1;
if (z1 < z2) return 1;
return 0;
}
If you know each version number will have exactly 3 integers separated by dots, you can parse them (e.g. using sscanf(3)) and compare them:
const char *version1str = "1.0.1";
const char *version2str = "1.2.5";
int major1, minor1, patch1;
int major2, minor2, patch2;
if(sscanf(version1str, "%d.%d.%d", &major1, &minor1, &patch1) == 3 &&
sscanf(version2str, "%d.%d.%d", &major2, &minor2, &patch2) == 3)
{
// Parsing succeeded, now compare the integers
if(major1 > major2 ||
(major1 == major2 && (minor1 > minor2 ||
(minor1 == minor2 && patch1 > patch2))))
{
// version1 > version2
}
else if(major1 == major2 && minor1 == minor2 && patch1 == patch2)
{
// version1 == version2
}
else
{
// version1 < version2
}
}
else
{
// Handle error, parsing failed
}
To check the version in swift you can use following
switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) {
case .OrderedDescending:
println("NewVersion available ")
// Show Alert Here
case .OrderedAscending:
println("NewVersion Not available ")
default:
println("default")
}
Hope it might be helpful.
Here is a recursive function that do the works with multiple version formatting of any length. It also works for #"1.0" and #"1.0.0"
static inline NSComparisonResult versioncmp(const NSString * a, const NSString * b)
{
if ([a isEqualToString:#""] && [b isEqualToString:#""]) {
return NSOrderedSame;
}
if ([a isEqualToString:#""]) {
a = #"0";
}
if ([b isEqualToString:#""]) {
b = #"0";
}
NSArray<NSString*> * aComponents = [a componentsSeparatedByString:#"."];
NSArray<NSString*> * bComponents = [b componentsSeparatedByString:#"."];
NSComparisonResult r = [aComponents[0] compare:bComponents[0] options:NSNumericSearch];
if(r != NSOrderedSame) {
return r;
} else {
NSString* newA = (a.length == aComponents[0].length) ? #"" : [a substringFromIndex:aComponents[0].length+1];
NSString* newB = (b.length == bComponents[0].length) ? #"" : [b substringFromIndex:bComponents[0].length+1];
return versioncmp(newA, newB);
}
}
Test samples :
versioncmp(#"11.5", #"8.2.3");
versioncmp(#"1.5", #"8.2.3");
versioncmp(#"1.0", #"1.0.0");
versioncmp(#"11.5.3.4.1.2", #"11.5.3.4.1.2");
Based on #nathan-de-vries 's answer, I wrote SemanticVersion.swift for comparing Semantic Version, and here is the test cases.