iOS 滑块拼图游戏(Puzzle8)

来源:互联网 发布:常用数据快速录入 编辑:程序博客网 时间:2024/05/16 06:34

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!


效果图


一、准备工作


先了解一个定义和定理


定义:在一个1,2,...,n的排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。——这是北大《高等代数》上的定义。


定理:交换一个排列中的两个数,则排列的奇偶性发生改变。


二、实现过程


以3*3拼图为例进行分析


1、随机打乱拼图


1)初始化从0-8的数组initializeNums


NSMutableArray *initializeNums = [NSMutableArray array];//初始化0-n数字

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

    [initializeNums addObject:@(i)];

}


2)从initializeNums随机抽取数字add到数组randomNums,得到随机数组


NSMutableArray *randomNums = [NSMutableArray array];//随机数组

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

    int randomNum = arc4random() % initializeNums.count;

    [randomNums addObject:initializeNums[randomNum]];

    [initializeNums removeObjectAtIndex:randomNum];   

}


3)判断拼图是否可还原


① 图1,通过移动要还原到的拼图状态

② 图2,随机打乱的拼图状态

③ 图3,将图2中的空白块移动到拼图右下角的拼图状态,用来计算打乱的拼图是否可以还原

④ 空白块处相当于数字8

⑤ 我们的目的是把打乱拼图如图2通过移动(空白块与相邻数字块位置交换)还原到图1状态

⑥ 不是每个随机打乱的拼图都能还原到图1状态(根据定义和定理有50%概率随机打乱的拼图不能还原)

⑦ 根据定义和定理 ,图1的逆序数为0,为偶排列。所以只有图3也为偶排列,图2才有可能还原到图1状态


图1


图2


如何计算图3的逆序数


① 先计算图2的逆序数

② 再计算图2到图3变换步数

③ 将两者相加即得图3逆序数


图3


判断图2是否可还原代码:


//判断是否可还原拼图

inverCount = 0;

int curNum = 0;

int nextNum = 0;

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

    curNum = [randomNums[i] intValue];

    if (curNum == _puzzleCount - 1) {

        inverCount += _difficulty - 1 - (i / _difficulty);

        inverCount += _difficulty - 1 - (i % _difficulty);

    }

    for (int j = i + 1; j < _puzzleCount; j++) {

        nextNum = [randomNums[j] intValue];

        if (curNum > nextNum) {

            inverCount++;

        }

    }


}

if (!(inverCount % 2)) {//对2求余,余0,逆序数为偶数,即偶排列;否则,为奇排列

    return randomNums;

}


获得随机可还原的数组randomNums


- (NSMutableArray *)getNewAvailableRandomNums {


    //随机数字

    int inverCount = 0;

    while (1) {

        NSMutableArray *initializeNums = [NSMutableArray array];//初始化0-n数字

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

            [initializeNums addObject:@(i)];

        }


        NSMutableArray *randomNums = [NSMutableArray array];//随机数组

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


            int randomNum = arc4random() % initializeNums.count;


            [randomNums addObject:initializeNums[randomNum]];


            [initializeNums removeObjectAtIndex:randomNum];


        }

        //判断是否可还原拼图

        inverCount = 0;

        int curNum = 0;

        int nextNum = 0;

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

            curNum = [randomNums[i] intValue];

            if (curNum == _puzzleCount - 1) {

                inverCount += _difficulty - 1 - (i / _difficulty);

                inverCount += _difficulty - 1 - (i % _difficulty);

            }

            for (int j = i + 1; j < _puzzleCount; j++) {

                nextNum = [randomNums[j] intValue];

                if (curNum > nextNum) {

                    inverCount++;

                }

            }


        }

        if (!(inverCount % 2)) {//对2求余,余0,逆序数为偶数,即偶排列;否则,为奇排列

            return randomNums;

        }


    }

}


2、初始化拼图UI (九宫格)


代码:


- (void)customUI {

    CGFloat puzzleBgViewX = 0;

    CGFloat puzzleBgViewY = 64 + 20;

    CGFloat puzzleBgViewW = [UIScreen mainScreen].bounds.size.width;

    CGFloat puzzleBgViewH = puzzleBgViewW;


    _puzzleBgView = [[UIView alloc] initWithFrame:CGRectMake(puzzleBgViewX, puzzleBgViewY, puzzleBgViewW, puzzleBgViewH)];

    _puzzleBgView.backgroundColor = [UIColor lightGrayColor];

    [self.view addSubview:_puzzleBgView];


    CGFloat puzzleBtnX = 0;

    CGFloat puzzleBtnY = 0;

    CGFloat puzzleBtnW = puzzleBgViewW / _difficulty - kPuzzleBtnGap * 2;

    CGFloat puzzleBtnH = puzzleBtnW;


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

        puzzleBtnX = i % _difficulty * (puzzleBtnW + kPuzzleBtnGap * 2) + kPuzzleBtnGap;

        puzzleBtnY = i / _difficulty * (puzzleBtnH + kPuzzleBtnGap * 2) + kPuzzleBtnGap;

        UIButton *puzzleBtn = [UIButton buttonWithType:UIButtonTypeCustom];

        puzzleBtn.frame = CGRectMake(puzzleBtnX, puzzleBtnY, puzzleBtnW, puzzleBtnH);

        puzzleBtn.tag = i;

        puzzleBtn.clipsToBounds = YES;

        [_puzzleBgView addSubview:puzzleBtn];

        int  puzzleValue = [self.randomNums[i] intValue];

        if (puzzleValue == _puzzleCount - 1) {

            puzzleBtn.backgroundColor = [UIColor clearColor];

            _maxPuzzleBtn = puzzleBtn;

        } else {

                [puzzleBtn setTitle:[NSString stringWithFormat:@"%d", puzzleValue + 1] forState:UIControlStateNormal];

                puzzleBtn.backgroundColor = [UIColor colorWithRed:0x4A / 255.0 green:0xC2 / 255.0 blue:0xFB / 255.0 alpha:1];

            [puzzleBtn addTarget:self action:@selector(puzzleBtnAction:) forControlEvents:UIControlEventTouchUpInside];

        }

    }

}


3、滑块移动逻辑


点击空白块周围数字块,数字块移到空白块区域(其实就是空白块和数字块交换)


图4


index:数字块对应位置如图4

_difficulty : 拼图列数

③ 点击数字块依次判断其上 下 左 右 是否有空白块

④ 找到空白块,将点击数字块与空白块位置交换,实现数字块移动效果


以数字块3(index = 4)为例分析


上 :upIndex = index - _difficulty 判断是否在九宫格里&&其位置对应的值是否是8,即空白块。


upIndex >= 0 && [self.randomNums[upIndex] intValue] == _puzzleCount - 1


下:downIndex = index + _difficulty 判断是否在九宫格里&&其位置对应的值是否是8,即空白块。


if (downIndex <= _puzzleCount - 1 && [self.randomNums[downIndex] intValue] == _puzzleCount - 1


左:leftIndex = index - 1 判断是否在九宫格里&&其位置对应的值是否是8,即空白块


index % _difficulty > 0 && [self.randomNums[leftIndex] intValue] == _puzzleCount - 1


右 :rightIndex = index + 1 判断是否在九宫格里&&其位置对应的值是否是8,即空白块


index % _difficulty < _difficulty - 1 && [self.randomNums[rightIndex] intValue] == _puzzleCount - 1


代码:


- (void)puzzleBtnAction:(UIButton *)puzzleBtn {

    NSInteger index = puzzleBtn.tag;


    //上

    NSInteger upIndex = index - _difficulty;

    if (upIndex >= 0 && [self.randomNums[upIndex] intValue] == _puzzleCount - 1) {


        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;

        CGPoint puzzleBtnCenter = puzzleBtn.center;

        _maxPuzzleBtn.tag = index;

        puzzleBtn.tag = upIndex;

        self.randomNums[upIndex] = @([self.randomNums[index] intValue]);

        self.randomNums[index] = @(_puzzleCount - 1);

        [UIView animateWithDuration:0.35 animations:^{

            puzzleBtn.center = maxPuzzleBtnCenter;

            _maxPuzzleBtn.center = puzzleBtnCenter;

        }];


        [self isWin];


        return;


    }

    //下

    NSInteger downIndex = index + _difficulty;

    if (downIndex <= _puzzleCount - 1 && [self.randomNums[downIndex] intValue] == _puzzleCount - 1) {

        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;

        CGPoint puzzleBtnCenter = puzzleBtn.center;

        _maxPuzzleBtn.tag = index;

        puzzleBtn.tag = downIndex;

        self.randomNums[downIndex] = @([self.randomNums[index] intValue]);

        self.randomNums[index] = @(_puzzleCount - 1);

        [UIView animateWithDuration:0.35 animations:^{

            puzzleBtn.center = maxPuzzleBtnCenter;

            _maxPuzzleBtn.center = puzzleBtnCenter;

        }];


        [self isWin];

        return;

    }

    //左

    NSInteger leftIndex = index - 1;

    if (index % _difficulty > 0 && [self.randomNums[leftIndex] intValue] == _puzzleCount - 1) {

        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;

        CGPoint puzzleBtnCenter = puzzleBtn.center;

        _maxPuzzleBtn.tag = index;

        puzzleBtn.tag = leftIndex;

        self.randomNums[leftIndex] = @([self.randomNums[index] intValue]);

        self.randomNums[index] = @(_puzzleCount - 1);

        [UIView animateWithDuration:0.35 animations:^{

            puzzleBtn.center = maxPuzzleBtnCenter;

            _maxPuzzleBtn.center = puzzleBtnCenter;

        }];


        [self isWin];

        return;

    }

    //右

    NSInteger rightIndex = index + 1;

    if (index % _difficulty < _difficulty - 1 && [self.randomNums[rightIndex] intValue] == _puzzleCount - 1) {

        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;

        CGPoint puzzleBtnCenter = puzzleBtn.center;

        _maxPuzzleBtn.tag = index;

        puzzleBtn.tag = rightIndex;

        self.randomNums[rightIndex] = @([self.randomNums[index] intValue]);

        self.randomNums[index] = @(_puzzleCount - 1);

        [UIView animateWithDuration:0.35 animations:^{

            puzzleBtn.center = maxPuzzleBtnCenter;

            _maxPuzzleBtn.center = puzzleBtnCenter;

        }];


        [self isWin];

        return;

    }


}


4、另一种打乱拼图的方法


思路:将图1经过有限次数随机移动达到打乱拼图的目的,这样打乱的拼图肯定是可还原的。


代码:


- (NSMutableArray *)getNewAvailableRandomNums2 {


   NSMutableArray *randomNums = [NSMutableArray array];//随机数组 - 初始化0-n数字

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

        [randomNums addObject:@(i)];

    }


    int randCount = _puzzleCount * _puzzleCount;

    int randDirection = 0; //0 上 1 下 2 左 3 右

    BOOL aliableDirection = NO;

    int blankIndex = 8;

    int index = 0;

    while (randCount--) {


        aliableDirection = NO;

        randDirection = arc4random() % 4;

        while (1) {

            switch (randDirection) {

                case 0:


                    if (blankIndex / _difficulty > 0) {

                        index = blankIndex - _difficulty;

                        aliableDirection = YES;

                    }

                    break;

                   case 1:

    

                    if (blankIndex / _difficulty < _difficulty - 1) {

                        index = blankIndex + _difficulty;

                        aliableDirection = YES;

                    }

                    break;

                case 2:


                    if (blankIndex % _difficulty > 0) {

                        index = blankIndex - 1;

                        aliableDirection = YES;

                    }

                    break;

                case 3:


                    if (blankIndex % _difficulty < _difficulty - 1) {

                        index = blankIndex + 1;

                        aliableDirection = YES;

                    }

                    break;

                default:

                    break;

            }

            if (aliableDirection == YES) {

                break;

            }

            randDirection = (randDirection + 1) % 4;

        }


        randomNums[blankIndex] = @([randomNums[index] intValue]);

        randomNums[index] = @(8);

        blankIndex = index;


    }

    return randomNums;

}


  • 本文链接:http://www.jianshu.com/p/9061bee560c4

  • iOS开发整理发布,转载请联系作者授权

【点击成为安卓大神】

原创粉丝点击