Как правильно рандомизировать буквы из NSString

Я создаю скремблер слов, и у меня возникают проблемы с рандомизацией букв. Когда буквы рандомизированы, это не имеет смысла.

Например, слово PARK отображается как AAPA. Таким образом, как вы можете сказать, для пользователя не будет смысла, когда пришло время расшифровывать.

Просто чтобы вы знали, я использую файл.plist для хранения слов.

Это код, который я использую для рандомизации букв:

    _words = [NSMutableArray arrayWithCapacity:scramblelength];

    for (int i=0;i<scramblelength;i++) { 

    NSString *letter = [scramble substringWithRange:[scramble rangeOfComposedCharacterSequenceAtIndex:arc4random()%[scramble length]]];

Затем я создаю UIImageViews для отображения зашифрованных слов:

    if (![letter isEqualToString:@""]) {
        GameView *boxes = [[GameView alloc] initWithLetter:letter andSideLength:boxSide];
        boxes.center = CGPointMake(xOffset + i*(boxSide + kTileMargin), kScreenHeight/4*3);

        [self.scrambleView addSubview:boxes];
        [_words addObject:boxes];

Что я здесь не так делаю? Я хотел бы, чтобы буквы в зашифрованных словах имели смысл.

Пожалуйста, помогите, я застрял на этом!

Спасибо!

3 ответа

Пока ваша длина строки будет соответствовать 32 битам, это должно быть хорошо. Если нет, я бы заменил arc4random_uniform с равномерным генератором случайных чисел в C++ и скомпилируйте его как модуль Objective-C++.

Код просто перебирает строку и заменяет каждую составленную последовательность символов на некоторую случайную составленную последовательность символов из той же строки.

Извините, вот что происходит, когда вы высокомерны и просто набираете код. Дайте мне знать, если у вас есть проблемы с этим...

Для гораздо больших строк есть более эффективный способ, но, похоже, это помогает.

NSMutableString категория...

@interface NSMutableString (Scramble)
- (void)scramble;
@end

@implementation NSMutableString (Scramble)
static void
swapRanges(NSMutableString *string, NSRange iRange, NSRange jRange)
{
    // Need to replace the "trailing" component first
    if (NSEqualRanges(iRange, jRange)) return;
    if (iRange.location > jRange.location) {
        NSRange tmpRange = iRange;
        iRange = jRange;
        jRange = tmpRange;
    }
    NSString *iString = [self substringWithRange:iRange];
    NSString *jString = [self substringWithRange:jRange];
    [string replaceCharactersInRange:jRange withString:iString];
    [string replaceCharactersInRange:iRange withString:jString];
}

- (void)scramble
{
    for (NSUInteger i = 0; i < self.length; ++i) {
        NSRange iRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
        NSUInteger j = arc4random_uniform(self.length);
        NSRange jRange = [self rangeOfComposedCharacterSequenceAtIndex:j];
        swapRanges(self, iRange, jRange);
    }
}
@end

NSString категория...

@interface NSString (Scramble)
- (NSString*)scrambledString;
@end
@implementation NSString (Scramble)
- (NSString *)scrambledString
{
    NSMutableString *result = [self mutableCopy];
    [result scramble];
    return [result copy];
}
@end

Пример использования...

[someMutableString scramble];

NSString *mixedUp = [someString scrambledString];

Или, если вы знакомы с C++, конвертировать в std::wstring, вызов std::random_shuffle, а затем преобразовать это в NSString, Гораздо меньше ошибок при использовании проверенного, хорошо проверенного кода.

Когда вы получаете случайную букву, вам нужно что-то сделать, чтобы удалить эту букву из вашего NSMutableArray (то есть буквы слова, когда они в порядке). Таким образом, когда вы перебираете слово, каждый раз остается меньше символов. Прямо сейчас из вашего ограниченного блока кода (первого) кажется, что вы этого не делаете. Вы хотите что-то вроде "[_words removeObjectAtIndex:letterIndex]", и вы также хотите выполнять итерацию от количества букв до нуля, поскольку вы удаляете элементы из массива также: for (int i=[_words count]; i > [_words count]; i--) потому что вам нужно перейти от 4 букв до 0 букв влево.

Итак, я уверен, что есть более эффективные способы сделать это, но я придерживаюсь правила не оптимизировать, пока вам это не нужно. Имея это в виду, этот код, кажется, работает правильно:

- (NSString *)scrambleWord:(NSString *)word {
    NSMutableArray *letterArray     = [self letterArrayFromWord:word];
    NSMutableString *returnValue    = [[NSMutableString alloc] init];
    do {
        int randomIndex = arc4random() % letterArray.count;
        [returnValue appendString:letterArray[randomIndex]];
        [letterArray removeObjectAtIndex:randomIndex];
        if (letterArray.count == 1) {
            [returnValue appendString:letterArray[0]];
            break;
        }
    } while (YES);
    if ([[returnValue copy] isEqualToString:word]) {
        return [self scrambleWord:word];

    } else {
        return [returnValue copy];

    }
}
- (NSMutableArray *)letterArrayFromWord:(NSString *)word {
    NSMutableArray *array = [NSMutableArray array];
    for (int i = 0; i < word.length; i = i + 1) {
        [array addObject:[NSString stringWithFormat:@"%C", [word characterAtIndex:i]]];
    }
    return array;
}
Другие вопросы по тегам