This question already has answers here:
Non repeating random numbers in Objective-C
(6 answers)
Closed 7 years ago.
I'm pretty new to coding, I've been looking for similar questions but none fits my needs. I'm working in a dice rolling app, and I need a a random number generator to "roll" the dice. arc4random seems perfect, but the problems that I can't have the same face occurring twice in a row. I have a method firing when I press the button with a timer
- (IBAction)dieRoll:(id)sender {
self.currentFace = 1;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(roll) userInfo:nil repeats:YES];;
}
but I have to implement the 'roll' method where I get a random number different from the one already selected (the property self.currentFace).
any clue?
this is how your implementation could look like:
#interface ViewController ()
#property (assign, nonatomic) NSInteger currentFace;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.currentFace = -1;
}
- (IBAction)diceRoll:(id)sender {
NSInteger newFace = -1;
do {
newFace = arc4random_uniform(6) + 1;
} while (newFace == self.currentFace);
self.currentFace = newFace;
}
#end
A simple solution would be to just remember the last rolled number and roll the dice until you get a different one. Pretty simple and you can keep arc4random.
An example:
- (NSUInteger)rollDiceWithLastFaceNumber:(NSUInteger)lastFaceNumber
{
NSUInteger currentFaceNumber;
do {
currentFaceNumber = (arc4random_uniform(6) + 1);
} while (currentFaceNumber == lastFaceNumber);
return currentFaceNumber;
}
and how to use it:
[self rollDiceWithLastFaceNumber:3];
This solution avoids an unknown number of iterations until you get your result.
- (void)roll {
NSUInteger next = [self nextFaceWithPreviousFace:self.currentFace];
NSLog(#"%lu", next);
self.currentFace = next;
}
- (NSUInteger)nextFaceWithPreviousFace:(NSUInteger)previous {
NSMutableArray *candidates = [NSMutableArray arrayWithObjects:#1, #2, #3, #4, #5, #6, nil];
[candidates removeObject:#(previous)];
NSUInteger index = arc4random_uniform((unsigned)candidates.count);
return [candidates[index] unsignedIntegerValue];
}
Related
I have an NSArray of 5 dice (dice1, dice2, dice3...). Once I have run the random number generator each dice1, dice2, dice3... can return a value between 1-6.
I would like to be able to count how many times a value of 1-6 has been returned.
I'm not too sure of the best way, whether I should turn the int number 1-6 into a string to match.
This is one of those situations where I don't feel that there is a particularly elegant solution due to the inability of Foundation types (such as NSCountedSet) to store intrinsic types (such as int). Swift's automatic boxing/unboxing of ints into NSNumber is a nice feature in this sort of situation.
Since you dealing with a small number of dice and a small number of possible values then you could ignore objects, sets and all that and just loop over your dice array, updating an array of integer counts.
The other, more complex, but object-oriented approach is to create a Die class:
Die.h
#import <Foundation/Foundation.h>
#interface Die : NSObject
-(instancetype)initWithSides:(NSUInteger)sides;
-(instancetype)initWithSides:(NSUInteger)sides initialValue:(NSUInteger)value;
-(NSUInteger)value;
-(NSUInteger)roll;
-(NSUInteger)sides;
#end
Die.m
#import "Die.h"
#interface Die ()
#property NSUInteger currentValue;
#property NSUInteger numberOfsides;
#end
#implementation Die
- (instancetype)initWithSides:(NSUInteger)sides {
NSAssert(sides>1, #"Dice must have at least 2 sides");
if (self = [super init]) {
self.numberOfsides = sides;
[self roll];
}
return self;
}
- (instancetype)initWithSides:(NSUInteger)sides initialValue:(NSUInteger)value {
NSAssert(sides>1, #"Dice must have at least 2 sides");
NSAssert(value <= sides, #"Initial value must not exceed number of sides");
if (self = [super init]) {
self.numberOfsides = sides;
self.currentValue = value;
}
return self;
}
- (NSUInteger)roll {
self.currentValue = arc4random_uniform((UInt32)self.numberOfsides)+1;
return self.currentValue;
}
- (NSUInteger)value {
return self.currentValue;
}
- (NSUInteger)sides {
return self.numberOfsides;
}
- (NSUInteger)hash {
return self.currentValue;
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Die class]]) {
return NO;
}
return [self isEqualToDie:(Die *)object];
}
- (BOOL) isEqualToDie:(Die *)otherDie {
return self.currentValue == otherDie.value;
}
#end
So now you have an object that can be stored in an NSCountedSet and you can retrieve the counts. This bit is slightly awkward since you need to compare to a Die with the appropriate value, not just the value itself:
// self.dice is an array of `Die` objects
NSCountedSet *valueCounts = [NSCountedSet setWithArray:self.dice];
for (int i=1;i<7;i++) {
NSUInteger count = [valueCounts countForObject:[[Die alloc] initWithSides:6 initialValue:i]];
NSLog(#"There are %lu dice showing %d",count,i);
}
use dictionaries :
let arrNum = [“one”, “two”, “three”, “two”]
var countNumber:[String:Int] = [:]
for item in arrNum {
countNumber[item] = (countNumber[item] ?? 0) + 1
}
for (key, value) in countNumber {
print("\(key) occurs \(value) time")
}
o/p :
one occurs 1 time
two occurs 2 time
three occurs 1 time
Basiclly, I have a button which created value according to finish some math process like +, - , /, *. Question is when I get some value, I needed to use that value again when I press button second time. Let me explain Basicly,
When I clicked button process like,
int examplea = [txt_example.text intValue];
int exB = 5000;
int exC = exB - examplea;
This is the first time I push the button, my lastest value is exC When I input text field another value and clicked it same process will start but one difference:
int examplea = [txt_example.text intValue];
int exB = exC;
int exC ( this new value which will be calculated ) = exB - examplea;
How can I create process like this?
Something like this
in the .h file
#interface MyCalculator : NSObject
- (int) doCalculation;
#property (assign, nonatomic) int exB;
#end
in the .m file
#implementation MyCalculator
- (id) init
{
self = [super init];
if (self)
{
_exB = 5000;
}
return self;
}
- (int) doCalculation
{
int examplea = [txt_example.text intValue];
int exC = self.exB - examplea;
self.exB = exC;
return exC;
}
#end
....
MyCalculator* myCalculator = [[MyCalculator alloc] init];
...
[myCalculator doCalculation];
This isn't the full solution as there are several questions about what your code will do, but it will illustrate the principle of how to do it.
The last line is what gets called when the button is pressed.
I don't know where txt_example.text is coming from, but it might be better if that is passed to doCalculation as a parameter.
so I'm creating a contact list app and I'm storing everything into an NSMutableArray. I've managed to get add function working perfectly. But I'm struggling with the Previous/Next functions.
I can get it to go back one object in the array when pressing previous, but i Can't get it to go back any further? here's my code: I have two extra classes, PhoneBookEntry which is a subclass of the Person class. These classes contain three strings, Firstname,lastname and studentID
- (IBAction)addPerson:(id)sender {
PhonebookEntry *person = [[PhonebookEntry alloc] init];
NSLog(#"%#",self.firstName.text);
person.firstName = self.firstName.text;
person.lastName = self.lastName.text;
person.phoneNumber = self.phoneNumber.text;
[self.entries addObject:person];
Here's my Previous button:
int length;
length = [self.entries count];
if (length > 0) {
int index;
index = [self.entries count] - 1;
NSLog (#"%d the index is", index);
NSLog(#"object %#", [self.entries objectAtIndex:index]);
} else {
NSLog (#"No contacts have been entered. No");
}
//NSLog(#"%d is the length - 1 hopefully", length);
//NSLog (#"index at %d is ", length);
I've tried removing the - 1 here:
index = [self.entries count] - 1;
and changing then on the next line putting index--; but nothing seems to work. it just goes back once.
I understand that length is getting the amount of objects in the index, and then -1 but shouldnt i-- at the end of the count / - 1 keep removing it everytime its pressed??
Any ideas? Cheers
You're going to keep hitting the same index with that piece of code - you need to store the state somewhere. Who is going to hold onto what the current index is? With a good designed system the model should really take care of this.
You are best off storing the current index into an ivar and updating that every time the button is pressed.
#interface OKAClass ()
#property (nonatomic, assign) NSInteger currentIndex;
#end
//... initialise the property with a default value
self.currentIndex = [self.entries count] - 1;
//... when the button is pressed decrement the index (you might want some min / max validation or use modulus to loop over and start from the top again)
NSLog(#"%#", self.entries[self.currentIndex--]);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am looking for a way to write a for loop that iterates over an NSDate. Each loop should increment the NSDate by 10 seconds.
I want to have 2 timestamps. Say timestamp A is Midnight Monday and timestamp B is Midnight Tuesday.
What I then want is some code to say for A to B incrementing at 10 second intervals between the two points in time, use the timestamp at current position, and the timestamp at the last position, so I can run a query based on the intervals.
Would someone be so good as to show me how I would do this?
Many thanks
A for loop needs three parts, an initialisation, a compare, and an increment. It could look like this:
for (NSDate *date = startDate; // initialisation
[date compare:endDate] == NSOrderedAscending; // compare
date = [date dateByAddingTimeInterval:10]) // increment
{
// do something with date here, eg:
NSDate *rangeStart = date;
NSDate *rangeEnd = [date dateByAddingTimeInterval:10];
[runQuery begin:rangeStart end:rangeEnd];
}
You might prefer to refactor to use a while loop so that the dateByAddingTimeInterval doesn't need to be repeated.
This is the same structure as a normal for loop:
for (int i = 0; // initialisation
i < 10; // compare
i++) // increment
{
// do something with i here
}
It sounds like you may want to look into NSTimer's timerWithTimeInterval. The interface looks like this:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
and then you can start one ten seconds later using:
initWithFireDate:interval:target:selector:userInfo:repeats:
You can sign up for a call that is sent every seconds as shown below:
// SomeClass.m
#import "SomeClass.h"
#interface ()
#property (nonatomic, strong) NSTimer timer1;
#property (nonatomic, strong) NSTimer timer2;
#end
#implementation SomeClass
- (id)init
{
self = [super init];
if (self) {
NSDate* date = [[NSDate date] dateByAddingTimeInterval:10];
self.timer1 = [NSTimer timerWithTimeInterval:10 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
self.timer2 = [[NSTimer alloc] initWithFireDate:date interval:10 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
}
return self;
}
- (void)timerFireMethod:(NSTimer *)timer
{
if (timer == self.timer1) {
NSLog(#"timer1 fired");
}
}
#end
I am making an application that randomly selects a picture from a preset group of pictures and displays it to a image view. This should happen every second or so until it has gone through 20 cycles.
Hear is my header and implementation code:
#interface spinController : UIViewController
{
IBOutlet UIImageView *imageHolder;
NSTimer *MovementTimer;
}
-(IBAction)Button:(id)sender;
-(void)displayPic;
#end
#implementation spinController
-(IBAction)Button:(id)sender
{
int count = 0;
while (count <20)
{
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(displayPic) userInfo:nil repeats:NO];
count++;
}
}
-(void)displayPic
{
int r = arc4random() % 2;
if(r==0)
{
imageHolder.image = [UIImage imageNamed:#"puppy1.jpg"];
}
else
{
imageHolder.image = [UIImage imageNamed:#"puppy2.jpg"];
}
}
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#end
I have made this application in a much more advanced form in WPF and ran into similar issues where the pictures do no cycle through properly. If I hit spin it randomizes but does not go through the 20 cycles... just one. This is my first application in objective-c and realize the efficiency of the method I choose will determine how good my application will run in a more complex form. Any help would be greatly appreciated.
The problem is that you're calling the timer repeatedly within the while loop; and since that particular while loop will complete within a millisecond or so, you're creating 20 timers one after another in immediate succession. Because of this, only the final image will show up in the imageHolder view. Edit: Even if the loop were to take more than a millisecond per iteration, I believe the NSTimer wouldn't actually fire until the method exits anyway.
In order to show the images one after another as you're trying to do, (1) use the NSTimer without the while loop, (2) keep track of the iterations using a count class instance variable so as not to lose the value of the variable upon the completion of the various methods, and (3) pass along the NSTimer to the displayPic method so you can invalidate the timer from there upon the 20th iteration. For example:
// Declare the "count" instance variable.
int count;
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(IBAction)Button:(id)sender {
// The count starts at 0, so initialize "count" to 0.
count = 0;
// Use an NSTimer to call displayPic: repeatedly every 1 second ("repeats" is set to "YES")
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(displayPic:) userInfo:nil repeats:YES];
}
// Pass along the NSTimer to the displayPic method so that it can be invalidated within this method upon the 20th iteration
-(void)displayPic:(NSTimer *)timer {
// Get the random number
int r = arc4random() % 2;
// If the number is 0, display puppy1.jpg, else display puppy2.jpg.
if(r == 0) {
imageHolder.image = [UIImage imageNamed:#"puppy1.jpg"];
}
else {
imageHolder.image = [UIImage imageNamed:#"puppy2.jpg"];
}
// Increment "count" to reflect the number of times the NSTimer has called this method since the button press
count ++;
// If the count == 20, stop the timer.
if (count == 20)
[timer invalidate];
}
#end
Change repeats to YES. This causes the timer to run again and again. Then instead of a while loop check the count in the method itself.