精华内容
下载资源
问答
  • 此次博客是深鸿会博主(Zzt_01-23)学习完HarmonyOS(鸿蒙)后,将前两个自行开发的鸿蒙小游戏合并个app,两个小游戏分别为黑白翻棋和数字华容道,此处将不再详细讲述这两个小游戏的开发,读者可以自行前往观看...

    前言

    此次博客是深鸿会博主(Zzt_01-23)学习完HarmonyOS(鸿蒙)后,将前两个自行开发的鸿蒙小游戏合并为一个app,两个小游戏分别为黑白翻棋和数字华容道,此处将不再详细讲述这两个小游戏的开发,读者可以自行前往观看相应的博客:从零开发HarmonyOS(鸿蒙)运动手表小游戏——黑白翻棋从零开发HarmonyOS(鸿蒙)运动手表小游戏——数字华容道,本博客详细讲述了游戏合并的开发思路,适合学习完前两篇博客的读者,使读者能自行将不同的小游戏合并为一个app。

    概述

    本个demo将从零开始完成两个鸿蒙小游戏在可穿戴设备上的编译合并为一个app,此处以运动手表为例,在项目中我们所使用到的软件为DevEco Studio,下载地址为:DevEco Studio下载DevEco Studio安装教程,在项目中我们要实现的内容为两个鸿蒙小游戏合并为一个app。

    1. 在初始界面显示数字华容道游戏的初始界面,向左或者向右滑动便会切换到黑白翻棋的游戏的初始界面,再次向左或者向右滑动便会切换到数字华容道游戏的初始界面。
      在这里插入图片描述

    在这里插入图片描述
    2. 在各自游戏的初始界面点击开始游戏便会切换到各自对应的游戏界面,在游戏界面点击返回便会切换到对应的游戏开始界面。
    在这里插入图片描述
    在这里插入图片描述

    正文

    创建项目

    DevEco Studio下载安装成功后,打开DevEco Studio,点击左上角的File,点击New,再选择New Project,选择Lite Wearable选项,选择默认的模板,然后选择保存路径,将文件命名为YouXi(文件名不能出现中文或者特殊字符,否则将无法成功创建项目文件),最后点击Finish。
    在这里插入图片描述
    在这里插入图片描述
    主要编写的文件为index.css、index.hml和index.js,打开路径如图所示,index.hml用于描述页面中包含哪些组件,index.css用于描述页面中的组件都长什么样,index.js用于描述页面中的组件是如何进行交互的。
    在这里插入图片描述

    实现初始界面的布局

    首先我们显示数字华容道游戏和黑白翻棋游戏的初始界面
    在这里插入图片描述
    在这里插入图片描述

    1. 我们在index文件里编写数字华容道游戏的初始界面,所以我们还需要一个文件去编写黑白翻棋游戏的初始界面,故右击选择pages,选择New,再选择JS Page,将其命名为heibaifanqi,最后点击Finish即可,这样我们在右侧pages文件里就创建好了heibaifanqi文件用于编写黑白翻棋游戏的初始界面。在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    2. 我们要编写两个游戏的初始界面,就需要插入初始界面的图片,这样就需要一个文件存放初始界面的图片,先右击选择default,选择New,再选择Directory,将其命名为common,最后点击OK即可,这样我们在右侧default文件里就创建好了common文件用于存放初始界面的图片。
      在这里插入图片描述
      在这里插入图片描述
      我们还需要将初始界面的图片放入刚刚新建的文件common里,分别复制准备好的初始界面的图片,右击选择common,再选择Paste,将其命名为hm1和hm2,这样就完成所有的准备工作了。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    3. 在index.hml中添加相应的页面组件:
      首先创建一个基础容器div类名为container,用于盛装所有的其他组件
    <div class="container" >
    </div>
    

    然后在此基础容器中添加一个图片组件imaget类名为img,图片路径为/common/hm1.png

    <image src="/common/hm1.png" class="img"></image>
    

    至此,index.hml文件已经全部编写完毕

    <div class="container">
        <image src="/common/hm1.png" class="img"></image>
    </div>
    
    1. 在index.css中描述刚才添加的页面组件的样式:
      首先编写container的样式,flex-direction为容器主轴方向,选择column(垂直方向从上到下),justify-content为容器当前行的主轴对齐格式,选择center(项目位于容器的中心),align-items为容器当前行的交叉轴对齐格式,选择center(元素在交叉轴居中),width、height分别为容器以像素为单位的宽度和高度,都设定为450px
    .container {
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width:450px;
        height:450px;
    }
    

    然后编写img样式,left为指示距边界框左上角的以像素为单位的水平坐标,设定为8px,top为指示距边界框左上角的以像素为单位的垂直坐标,设定为50px,由于图片大小为450*358,所以width设定为450px,height设定为358px

    .img{
        left:8px;
        top:50px;
        width:450px;
        height: 358px;
    }
    

    至此,index.css文件已经全部编写完毕
    5. 在index.js中描述页面中的组件交互情况:
    由于暂不需要组件交互,将title:‘Word’删除即可

    export default {
        data: {
    
        }
    }
    

    至此,index.jd文件已经全部编写完毕,运行即可得出数字华容道游戏初始界面布局了
    6. 由于黑白翻棋游戏初始界面布局与华容道游戏初始界面布局类似,故就不一一叙述了,直接上源代码
    heibaifanqi.hml代码如下:

    <div class="container">
        <image src="/common/hm2.png" class="img"></image>
    </div>
    

    heibaifanqi.css代码如下:

    .container {
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width:450px;
        height:450px;
    }
    .img{
        left: 80px;
        top: 70px;
        width:290px;
        height: 347px;
    }
    

    heibaifanqi.js代码如下:

    export default {
        data: {
    
        }
    }
    

    实现初始界面的切换

    华容道游戏初始界面向左或者向右滑动便会切换到黑白翻棋游戏初始界面,再次向左或者向右滑动便会切换到数字华容道游戏初始界面
    在这里插入图片描述
    在这里插入图片描述

    1. 首先打开config.json文件,在js栏的pages里添加"pages/heibaifanqi/heibaifanqi"
      在这里插入图片描述
    2. 在index.hml中添加相应的页面组件:
      在基础容器div中添加一个swipe属性,用于响应滑动事件,赋予一个所自动调用的函数changeGame
    <div class="container" onswipe="changeGame">
        <image src="/common/hm1.png" class="img"></image>
    </div>
    

    至此,index.hml文件已经全部编写完毕
    3. 在index.css中描述刚才添加的页面组件的样式:
    这一部分不需要添加或修改页面组件的样式
    4. 在index.js中描述页面中的组件交互情况:
    页面跳转语句为router.replace,为此我们需要导入router,创建一个函数Game(direction),参数为滑动方向,当滑动方向为左或者右时,调用语句router.replace,跳转到heibaifanqi,再创建一个函数changeGame(event),参数为一个事件,用于调用函数Game

    import router from '@system.router';
    
    changeGame(event){
            this.Game(event.direction);
        },
        Game(direction){
            if (direction == 'left' || direction == 'right'){
                router.replace({
                    uri:'pages/heibaifanqi/heibaifanqi'
                });
            }
        }
    

    至此,index.jd文件已经全部编写完毕,运行即可实现华容道游戏初始界面向左或者向右滑动便会切换到黑白翻棋游戏的初始界面
    5. 黑白翻棋游戏初始界面向左或者向右滑动便会切换到华容道游戏初始界面和上述编写方法类似,故就不一一叙述了,直接上源代码
    heibaifanqi.js代码如下:

    <div class="container"  onswipe="changeGame">
        <image src="/common/hm2.png" class="img"></image>
    </div>
    

    heibaifanqi.css代码没有更改的
    heibaifanqi.js代码如下:

    import router from '@system.router';
    
    export default {
        data: {
    
        },
        changeGame(event){
            this.Game(event.direction);
        },
        Game(direction){
            if (direction == 'left' || direction == 'right'){
                router.replace({
                    uri:'pages/index/index'
                });
            }
        }
    }
    

    实现初始界面切换到游戏界面

    在各自游戏的初始界面点击开始游戏便会切换到各自对应的游戏界面
    在这里插入图片描述
    在这里插入图片描述

    1. 我们需要两个文件去编写数字华容道游戏和黑白翻棋游戏的游戏界面,故右击选择pages,选择New,再选择JS Page,分别将其命名为game1和game2,最后点击Finish即可,这样我们在右侧pages文件里就创建好了game1和game2两个文件用于编写数字华容道游戏和黑白翻棋游戏的游戏界面。
      在这里插入图片描述

    2. 将数字华容道游戏的代码和黑白翻棋游戏的代码分别复制粘贴到game1文件和game2文件里,代码可以在这里找:从零开发HarmonyOS(鸿蒙)运动手表小游戏——数字华容道从零开发HarmonyOS(鸿蒙)运动手表小游戏——黑白翻棋,并且打开config.json文件,在js栏的pages里添加"pages/game1/game1"和"pages/game2/game2"
      在这里插入图片描述

    3. 在index.hml中添加相应的页面组件:
      我们需要在数字华容道上显示一个按钮,用于跳转到数字华容道游戏界面,故需要将图片组件放到栈中,所以添加一个栈stack类名为stack,还需要一个按钮组件button类名为bit,增加一个点击事件click,并且使图片组件先进栈,按钮组件后进栈,这样就能达到预期效果了

    <div class="container" onswipe="changeGame">
        <stack class="stack">
            <image src="/common/hm1.png" class="img"></image>
            <input type="button" class="bit" onclick="startGame"/>
        </stack>
    </div>
    

    至此,index.hml文件已经全部编写完成了
    4. 在index.css编写刚才添加组件的样式:
    首先编写stack的样式,width和height都设定为450px

    .stack{
        width: 450px;
        height: 450px;
    }
    

    然后编写bit的样式,top设定为338px,left设定为118px,width设定为210px,height设定为50px,background-color为背景颜色,设定为transparent(透明)

    .bit{
        top:338px;
        left:118px;
        width:210px;
        height:50px;
        background-color:transparent;
    }
    

    至此,index.css文件已经全部编写完毕
    5. 在index.js中描述页面中的组件交互情况:
    我们需要导入router,编写点击按钮时所自动调用的函数startGame(),调用语句router.replace,跳转到game1

    import router from '@system.router';
    
    startGame(){
            router.replace({
                uri:'pages/game1/game1'
            });
        }
    

    至此,index.js文件已经全部编写完毕,运行即可实现华容道游戏初始界面点击开始游戏便会切换到华容道游戏界面
    6. 黑白翻棋游戏初始界面点击开始游戏便会切换到黑白翻棋游戏界面和上述编写方法类似,故就不一一叙述了,直接上源代码
    heibaifanqi.hml代码如下:

    <div class="container" onswipe="changeGame">
        <stack class="stack">
            <image src="/common/hm2.png" class="img"></image>
            <input type="button" class="bit" onclick="startGame"/>
        </stack>
    </div>
    
    

    heibaifanqi.css代码如下:

    .container {
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width:450px;
        height:450px;
    }
    .stack{
        width: 450px;
        height: 450px;
    }
    .img{
        left: 80px;
        top: 70px;
        width:290px;
        height: 347px;
    }
    .bit{
        top:358px;
        left:118px;
        width:210px;
        height:50px;
        background-color:transparent;
    }
    
    

    heibaifanqi.js代码如下:

    import router from '@system.router';
    export default {
        data: {
    
        },
        changeGame(event){
            this.Game(event.direction);
        },
        Game(direction){
            if (direction == 'left' || direction == 'right'){
                router.replace({
                    uri:'pages/index/index'
                });
            }
        },
        startGame(){
            router.replace({
                uri:'pages/game2/game2'
            });
        }
    }
    

    实现游戏界面切换到初始界面

    在各自游戏的界面点击返回便会切换到各自对应的初始界面
    在这里插入图片描述
    在这里插入图片描述

    1. 在game1.hml中添加相应的页面组件:
      我们需要再添加一个按钮button类名为bit2,增加一个响应点击事件click和自动调用的函数returnGame,用于返回到游戏初始界面,并且要使两个按钮水平排列,需要将这两个按钮另放到一个基础容器div类名为container2中
    <div class="container2">
            <input type="button" value="重新开始" class="bit1" onclick="restartGame"/>
            <input type="button" value="返回" class="bit2" onclick="returnGame"/>
        </div>
    

    至此,game1.hml文件已经全部编写完毕
    2. 在game1.css中描述刚才添加的页面组件的样式:
    首先编写container2的样式,flex-direction、选择column(垂直方向从上到下),justify-content、选择center(项目位于容器的中心),align-items、选择center(元素在交叉轴居中),width、height、设定为450px、40px

    .container2{
        flex-direction: row;
        justify-content: center;
        align-items: center;
        width:450px;
        height:40px;
    }
    

    然后编写bit2的样式,width、height分别设定为80px和30px,background-color设定为#AD9D8F,font-size设定为24px,margin-top设定为10px, margin-left:设定为10px

    .bit2{
        width:80px;
        height:30px;
        margin-left: 10px;
        background-color:#AD9D8F;
        font-size:24px;
        margin-top:10px;
    }
    

    至此,game1.css文件已经全部编写完毕
    3. 在game1.js中描述页面中的组件交互情况:
    我们需要导入router,编写点击按钮时所自动调用的函数returnGame(),调用语句router.replace,跳转到index,并且要调用clearInterval清除计时器

    import router from '@system.router';
    
    returnGame(){
            router.replace({
                uri:'pages/index/index'
            });
            clearInterval(timer);
        }
    

    至此,game1.js文件已经全部编写完毕,运行即可实现华容道游戏界面点击返回便会切换到华容道游戏初始界面
    4. 黑白翻棋游戏界面点击返回便会切换到黑白翻棋游戏初始界面和上述编写方法类似,故就不一一叙述了,直接上源代码
    game2.hml修改和增加代码如下:

    <div class="container2">
            <input type="button" value="重新开始" class="bit1" onclick="restartGame"/>
            <input type="button" value="返回" class="bit2" onclick="returnGame"/>
        </div>
    

    game2.css增加代码如下:

    .container2{
        flex-direction: row;
        justify-content: center;
        align-items: center;
        width:450px;
        height:40px;
    }
    .bit2{
        width:80px;
        height:30px;
        margin-left: 10px;
        background-color:#AD9D8F;
        font-size:24px;
        margin-top:10px;
    }
    

    game2.js增加代码如下:

    import router from '@system.router';
    
    returnGame(){
            router.replace({
                uri:'pages/heibaifanqi/heibaifanqi'
            });
        }
    

    至此,整个demo也全部完成了

    心得

    本个demo整体难度不高,涉及的组件或样式都是很常见的,需要掌握图片组件、页面跳转即可完成编写,适合刚入门的读者编写与学习,其中组件学习可以前往官方文档查看更详细的介绍:官方文档

    结语

    本次项目为博主学习了鸿蒙一些基础后自行编写完成的,感兴趣的读者可以自行跟着本博客编写,相信你们也能够完成的。如果有遇到什么问题,或者查找出其中的错误之处,欢迎留言,本博主也欢迎与各位感兴趣的读者一起学习HarmonyOS(鸿蒙)开发。

    展开全文
  • 如果只是单纯的写个2048游戏,让这个游戏可以玩...不过,在这写工作中,你可能花时间最多的就是数字的移动与合并的算法了,如果没有做过,可能确实要花点时间来构思,所以,写完2048游戏以后,我希望能把它做个记录。

    如果只是单纯的写一个2048游戏,让这个游戏可以玩的话,工作量还是蛮小的。不过,在这写工作中,你可能花时间最多的就是数字的移动与合并的算法了,如果没有做过,可能确实要花点时间来构思,所以,写完2048游戏以后,我希望能把它做个记录。

    移动与合并的算法

    比如说我们有如下一个界面:
    这里写图片描述
    现在,玩家向左划,这个导致所有的数字向左移动,并且移动的过程中如果发生碰撞,会检查数字是不是可以合并。
    我们的算法应该是通用的,不仅对于4*4模式,即便是针对3*3模式,n*n模式,它都应该是一样的。
    那么怎么做呢?其实就两步:
    第一步:把第一个空格和空格后面的第一个数字(如果有)交换。
    第二步:交换后检查需不需要合并。
    以此类推。
    为了便于陈述,我们给图片做一个坐标:
    这里写图片描述
    在这张图片中,按照我们说的,第一个空白和第一个数字交换,也就是把(2,C)和(3,C)交换,交换后检查能不能合并,如果能则合并,不过不能则不合并,这里显然可以合并,所以我们把他们合并为4.然后空白后面就没有数字了,算法结束。
    因此,我们的算法必须记录第一个空白的位置和第一个数字的位置,那么我们用k记录空白,用j记录第一个数字,然后对于每一行,从左向右做这样的事情。
    直接上代码吧,结合代码一说就会明白:
    首先,我们的一个数字使用一个Number类来表述:

    public class Number {
        public int mScores;
        public int mCurPosition;
        public int mBeforePosition;
        public boolean isNeedMove;
        public boolean isNeedCombine;
        public Number(int position,int scores){
            mScores = scores;
            mCurPosition = mBeforePosition = position;
            isNeedMove = false;
            isNeedCombine = false;
        }
        public void reset(){
            mScores = 0;
            isNeedMove = false;
            isNeedCombine = false;
        }
    }

    可见一个Number中有scores,也就是分数,当前的位置和之前的位置是用来计算动画的,我们需要把一个Number从之前的位置移动到当前的位置。
    然后整个游戏使用一个Numbers类来表述:

    public class Numbers {
        Number [][] mNumbers = new Number[Game2048StaticControl.gamePlayMode][Game2048StaticControl.gamePlayMode];
        public Numbers(){
            for(int i=0;i<Game2048StaticControl.gamePlayMode;i++){
                for(int j=0;j<Game2048StaticControl.gamePlayMode;j++){
                    mNumbers[i][j] = new Number(0,0);
                }
            }
        }
        public Number getNumber(int x,int y){
            return mNumbers[x][y];
        }
        public Number [][] getNumbers (){
            return mNumbers;
        }
        public int getBlankCount(){
            int count = 0;
            for(int i=0;i<Game2048StaticControl.gamePlayMode;i++){
                for(int j=0;j<Game2048StaticControl.gamePlayMode;j++){
                    if(mNumbers[i][j].mScores==0){
                        count++;
                    }
                }
            }
            return count;
        }
        public int getPositonFromBlankCountTh(int blankTh){
            int count = 0;
            for(int i=0;i<Game2048StaticControl.gamePlayMode;i++){
                for(int j=0;j<Game2048StaticControl.gamePlayMode;j++){
                    if(mNumbers[i][j].mScores==0){
                        if(count==blankTh){
                            return i*Game2048StaticControl.gamePlayMode+j;
                        }else {
                            count++;
                        }
                    }
                }
            }
            return -1;
        }
        public void swapNumber(int position1,int position2){
            mNumbers[position1/Game2048StaticControl.gamePlayMode][position1%Game2048StaticControl.gamePlayMode].mCurPosition = position2;
            mNumbers[position1/Game2048StaticControl.gamePlayMode][position1%Game2048StaticControl.gamePlayMode].mBeforePosition = position1;
            mNumbers[position2/Game2048StaticControl.gamePlayMode][position2%Game2048StaticControl.gamePlayMode].mCurPosition = position1;
            mNumbers[position2/Game2048StaticControl.gamePlayMode][position2%Game2048StaticControl.gamePlayMode].mBeforePosition = position2;
            Number tem = mNumbers[position1/Game2048StaticControl.gamePlayMode][position1%Game2048StaticControl.gamePlayMode];
            mNumbers[position1/Game2048StaticControl.gamePlayMode][position1%Game2048StaticControl.gamePlayMode] = mNumbers[position2/Game2048StaticControl.gamePlayMode][position2%Game2048StaticControl.gamePlayMode];
            mNumbers[position2/Game2048StaticControl.gamePlayMode][position2%Game2048StaticControl.gamePlayMode] = tem;
        }
    }

    这个类的核心就是一个Number [n][n]的数组,n可以为任意值,因为我们的算法是通用的。

    有了这个的概念以后,我们来看向左移动的算法,思想前面已经讲过了,直接看代码,结合代码非常容易理解。

       //return 0:do nothing
        //return 1:move
        //return 2:combine
        public int leftKeyDealAlgorithm(){
            int i, j, k;
            boolean isMoved = false;
            boolean isFinalMove = false;
            boolean isFinalCombie = false;
            for(i=0;i<Game2048StaticControl.gamePlayMode;i++){
                j=k=0;
                isMoved = false;
                while (true) {
                    while (j<Game2048StaticControl.gamePlayMode && !isPosionHasNumber(i,j))
                        j++;
                    if (j > Game2048StaticControl.gamePlayMode-1)
                        break;
                    if (j > k){
                        isMoved = true;
                        isFinalMove = true;
                        Number number = getNumber(i,j);
                        number.isNeedMove = true;
                        number.isNeedCombine = false;
                        swapNumber(i*Game2048StaticControl.gamePlayMode+k,i*Game2048StaticControl.gamePlayMode+j);
                    }
                    if (k > 0 && getNumber(i,k).mScores==getNumber(i,k-1).mScores && !getNumber(i,k-1).isNeedCombine){
                        isFinalCombie = true;
                        Number numberk = getNumber(i,k);
                        Number numberkl = getNumber(i,k-1);
                        if(isMoved){
                            numberkl.mBeforePosition = numberk.mBeforePosition;
                        }else {
                            numberkl.mBeforePosition =  i*Game2048StaticControl.gamePlayMode+k;
                        }
                        numberkl.mCurPosition = i*Game2048StaticControl.gamePlayMode+k-1;
                        numberkl.isNeedMove = true;
                        numberkl.isNeedCombine = true;
                        numberkl.mScores <<=1;
                        updateCurScoresAndHistoryScores(numberkl.mScores);
                        numberk.reset();
                        numberk.mCurPosition = numberk.mBeforePosition = i*Game2048StaticControl.gamePlayMode+k;
                    } else{
                        k++;
                    }
                    j++;
                }
            }
            return isFinalCombie?2:(isFinalMove?1:0);
        }
    

    第一步:j=k=0;
    第二步:找到第一个数字:

                    while (j<Game2048StaticControl.gamePlayMode && !isPosionHasNumber(i,j))
                        j++;

    第三步:如果j > k,那就意味这k这个位置的数字一定是空的,j这个位置的一定是第一个数字,所以就把他们交换。
    第四步:判断是不是需要合并

    if (k > 0 && getNumber(i,k).mScores==getNumber(i,k-1).mScores && !getNumber(i,k-1).isNeedCombine

    判断的条件是k>0,因为是向前合并,所以合并至少是从第二个开始的,其次就是两个数字要相等,同时,已经合并了得数字不能再合并。
    然后再做下一个循环,如此往复即可完成。

    下面贴出其他三个方向的代码。

       public int rightKeyDealAlgorithm(){
            int i, j, k;
            boolean isMoved = false;
            boolean isFinalMove = false;
            boolean isFinalCombie = false;
            for(i=0;i<Game2048StaticControl.gamePlayMode;i++){
                j=k=Game2048StaticControl.gamePlayMode-1;
                isMoved = false;
                while (true) {
                    while (j>-1 && !isPosionHasNumber(i,j))
                        j--;
                    if (j < 0)
                        break;
                    if (j < k){
                        isMoved = true;
                        isFinalMove = true;
                        Number number = getNumber(i,j);
                        number.isNeedMove = true;
                        number.isNeedCombine = false;
                        swapNumber(i*Game2048StaticControl.gamePlayMode+k,i*Game2048StaticControl.gamePlayMode+j);
                    }
                    if (k < Game2048StaticControl.gamePlayMode-1 && getNumber(i,k).mScores==getNumber(i,k+1).mScores && !getNumber(i,k+1).isNeedCombine){
                        isFinalCombie = true;
                        Number numberk = getNumber(i,k);
                        Number numberkl = getNumber(i,k+1);
                        if(isMoved){
                            numberkl.mBeforePosition = numberk.mBeforePosition;
                        }else {
                            numberkl.mBeforePosition =  i*Game2048StaticControl.gamePlayMode+k;
                        }
                        numberkl.mCurPosition = i*Game2048StaticControl.gamePlayMode+k+1;
                        numberkl.isNeedMove = true;
                        numberkl.isNeedCombine = true;
                        numberkl.mScores <<=1;
                        updateCurScoresAndHistoryScores(numberkl.mScores);
                        numberk.reset();
                        numberk.mCurPosition = numberk.mBeforePosition = i*Game2048StaticControl.gamePlayMode+k;
                    } else{
                        k--;
                    }
                    j--;
                }
            }
            return isFinalCombie?2:(isFinalMove?1:0);
        }
        public int upKeyDealAlgorithm(){
            int i, j, k;
            boolean isMoved = false;
            boolean isFinalMove = false;
            boolean isFinalCombie = false;
            for(i=0;i<Game2048StaticControl.gamePlayMode;i++){
                j=k=0;
                isMoved = false;
                while (true) {
                    while (j<Game2048StaticControl.gamePlayMode && !isPosionHasNumber(j,i))
                        j++;
                    if (j > Game2048StaticControl.gamePlayMode-1)
                        break;
                    if (j > k){
                        isMoved = true;
                        isFinalMove = true;
                        Number number = getNumber(j,i);
                        number.isNeedMove = true;
                        number.isNeedCombine = false;
                        swapNumber(k*Game2048StaticControl.gamePlayMode+i,j*Game2048StaticControl.gamePlayMode+i);
                    }
                    if (k > 0 && getNumber(k,i).mScores==getNumber(k-1,i).mScores && !getNumber(k-1,i).isNeedCombine){
                        isFinalCombie = true;
                        Number numberk = getNumber(k,i);
                        Number numberkl = getNumber(k-1,i);
                        if(isMoved){
                            numberkl.mBeforePosition = numberk.mBeforePosition;
                        }else {
                            numberkl.mBeforePosition =  k*Game2048StaticControl.gamePlayMode+i;
                        }
                        numberkl.mCurPosition = (k-1)*Game2048StaticControl.gamePlayMode+i;
                        numberkl.isNeedMove = true;
                        numberkl.isNeedCombine = true;
                        numberkl.mScores <<=1;
                        updateCurScoresAndHistoryScores(numberkl.mScores);
                        numberk.reset();
                        numberk.mCurPosition = numberk.mBeforePosition = k*Game2048StaticControl.gamePlayMode+i;
                    } else{
                        k++;
                    }
                    j++;
                }
            }
            return isFinalCombie?2:(isFinalMove?1:0);
        }
        public int downKeyDealAlgorithm(){
            int i, j, k;
            boolean isMoved = false;
            boolean isFinalMove = false;
            boolean isFinalCombie = false;
            for(i=0;i<Game2048StaticControl.gamePlayMode;i++){
                j=k=Game2048StaticControl.gamePlayMode-1;
                isMoved = false;
                while (true) {
                    while (j>-1 && !isPosionHasNumber(j,i))
                        j--;
                    if (j < 0)
                        break;
                    if (j < k){
                        isMoved = true;
                        isFinalMove = true;
                        Number number = getNumber(j,i);
                        number.isNeedMove = true;
                        number.isNeedCombine = false;
                        swapNumber(k*Game2048StaticControl.gamePlayMode+i,j*Game2048StaticControl.gamePlayMode+i);
                    }
                    if (k < Game2048StaticControl.gamePlayMode-1 && getNumber(k,i).mScores==getNumber(k+1,i).mScores && !getNumber(k+1,i).isNeedCombine){
                        isFinalCombie = true;
                        Number numberk = getNumber(k,i);
                        Number numberkl = getNumber(k+1,i);
                        if(isMoved){
                            numberkl.mBeforePosition = numberk.mBeforePosition;
                        }else {
                            numberkl.mBeforePosition =  k*Game2048StaticControl.gamePlayMode+i;
                        }
                        numberkl.mCurPosition = (k+1)*Game2048StaticControl.gamePlayMode+i;
                        numberkl.isNeedMove = true;
                        numberkl.isNeedCombine = true;
                        numberkl.mScores <<=1;
                        updateCurScoresAndHistoryScores(numberkl.mScores);
                        numberk.reset();
                        numberk.mCurPosition = numberk.mBeforePosition = k*Game2048StaticControl.gamePlayMode+i;
                    } else{
                        k--;
                    }
                    j--;
                }
            }
            return isFinalCombie?2:(isFinalMove?1:0);
        }

    动画

    计算结束以后,我们需要使用动画移动和合并数字,因为都是直线运动,所以动画并不复杂,想想我们的Number类,计算动画只需要两个变量,一个之前的位置,一个是当前的位置。
    我们可以理一下思路:当用户需要向左移动时:

                        case Game2048StaticControl.DIRECT_LEFT:{
                            mNumberQueue.pushItem(mGAM.getmNumbers());
                            int ret =  mGAM.leftKeyDealAlgorithm();
                            if (ret>0){
                                startAnimation(mHolder,mPaint,Game2048StaticControl.DIRECT_LEFT);
                                mGAM.updateNumbers();
                                doDrawGameSurface();
                                sendEmptyMessage(Game2048StaticControl.GENERATE_NUMBER);
                                playSoundEffect(ret);
                            }
                            break;
                        }

    我们需要做如下几步:
    第一步:保存当前的游戏,用于反悔的时候回退。mNumberQueue.pushItem(mGAM.getmNumbers())
    第二步:计算移动与合并
    mGAM.leftKeyDealAlgorithm();
    第三步:使用动画移动和合并数字
    startAnimation(mHolder,mPaint,Game2048StaticControl.DIRECT_LEFT);
    第四步:生成一个新的数字
    sendEmptyMessage(Game2048StaticControl.GENERATE_NUMBER);
    通过发送消息来实现,具体的实现在消息的处理代码中,这很简单,这里暂不展开。
    下面看一个startAnimation方法。
    startAnimation定义如下:

        public void startAnimation(SurfaceHolder holder,Paint paint,int direct){
            int count = 0;
            RectF rectF = new RectF();
            while (count++<Game2048StaticControl.ANIMATION_MOVE_STEP) {
                Canvas canvas = holder.lockCanvas();
                mDrawTools.initSurfaceBg(canvas, paint);
                mDrawTools.drawSurfaceMap(canvas, paint);
                mDrawTools.drawSurfaceMapAndNumbersWhoIsNeedCombine(canvas,paint);
                for (int i = 0; i < Game2048StaticControl.gamePlayMode; i++) {
                    for (int j = 0; j < Game2048StaticControl.gamePlayMode; j++) {
                        mGAM.aniInsertValue(i, j, count, Game2048StaticControl.ANIMATION_MOVE_STEP,direct,rectF);
                        if(rectF != null && mGAM.isPosionHasNumber(i,j) && mGAM.getNumber(i,j).isNeedMove){
                            mDrawTools.drawNumberByRectF(i,j,canvas,paint,rectF);
                        }
                    }
                }
                holder.unlockCanvasAndPost(canvas);
            }
        }

    就是对每一个Number,使用 mGAM.aniInsertValue方法来计算它的坐标:

      public void aniInsertValue(int x,int y,int count,int insertCount,int direct,RectF rectF){
            Number number = getNumber(x,y);
            if(number.mCurPosition ==  number.mBeforePosition){
                return;
            }
            float xDiffPixels = Game2048StaticControl.GameNumberViewPosition[number.mCurPosition/Game2048StaticControl.gamePlayMode]
                    [number.mCurPosition%Game2048StaticControl.gamePlayMode].left
                    -Game2048StaticControl.GameNumberViewPosition[number.mBeforePosition/Game2048StaticControl.gamePlayMode]
                    [number.mBeforePosition%Game2048StaticControl.gamePlayMode].left;
            float yDiffPixels = Game2048StaticControl.GameNumberViewPosition[number.mCurPosition/Game2048StaticControl.gamePlayMode]
                    [number.mCurPosition%Game2048StaticControl.gamePlayMode].top
                    -Game2048StaticControl.GameNumberViewPosition[number.mBeforePosition/Game2048StaticControl.gamePlayMode]
                    [number.mBeforePosition%Game2048StaticControl.gamePlayMode].top;
            xDiffPixels = Math.abs(xDiffPixels);
            yDiffPixels = Math.abs(yDiffPixels);
            float xStep = xDiffPixels/insertCount;
            float yStep = yDiffPixels/insertCount;
            float xNewPosition = Game2048StaticControl.GameNumberViewPosition[number.mCurPosition/Game2048StaticControl.gamePlayMode]
                    [number.mCurPosition%Game2048StaticControl.gamePlayMode].left;
            float yNewPosition = Game2048StaticControl.GameNumberViewPosition[number.mCurPosition/Game2048StaticControl.gamePlayMode]
                    [number.mCurPosition%Game2048StaticControl.gamePlayMode].top;;
            switch (direct){
                case DIRECT_UP:{
                    yNewPosition = Game2048StaticControl.GameNumberViewPosition[number.mBeforePosition/Game2048StaticControl.gamePlayMode]
                            [number.mBeforePosition%Game2048StaticControl.gamePlayMode].top
                            - yStep*count;
                    break;
                }
                case DIRECT_DOWN:{
                    yNewPosition = Game2048StaticControl.GameNumberViewPosition[number.mBeforePosition/Game2048StaticControl.gamePlayMode]
                            [number.mBeforePosition%Game2048StaticControl.gamePlayMode].top
                                    + yStep*count;
                    break;
                }
                case DIRECT_LEFT:{
                    xNewPosition = Game2048StaticControl.GameNumberViewPosition[number.mBeforePosition/Game2048StaticControl.gamePlayMode]
                            [number.mBeforePosition%Game2048StaticControl.gamePlayMode].left
                            - xStep*count;
                    break;
                }
                case DIRECT_RIGHT:{
                    xNewPosition = Game2048StaticControl.GameNumberViewPosition[number.mBeforePosition/Game2048StaticControl.gamePlayMode]
                            [number.mBeforePosition%Game2048StaticControl.gamePlayMode].left
                            + xStep*count;
                    break;
                }
                default:break;
            }
            rectF.set(xNewPosition,yNewPosition,xNewPosition+Game2048StaticControl.gameNumberViewLength
                    ,yNewPosition+Game2048StaticControl.gameNumberViewLength);
        }

    计算的过程正对上下左右各不相同,原理非常简单:
    这里写图片描述
    原理就是在途中的花点的地方绘制一下数字就好了。也就是所谓的线性插值法。

    在随机位置随机生成2或者4

    生成2或者4就太简单了,随机位置怎么计算呢?这里要注意实在空白方格的随机位置哦,因此首先要获取当前有多少个空格:

        public int getBlankCount(){
            return mNumbers.getBlankCount();
        }

    进一步:

        public int getBlankCount(){
            int count = 0;
            for(int i=0;i<Game2048StaticControl.gamePlayMode;i++){
                for(int j=0;j<Game2048StaticControl.gamePlayMode;j++){
                    if(mNumbers[i][j].mScores==0){
                        count++;
                    }
                }
            }
            return count;
        }

    比如说当前有7个空白处,那么就只能生7以内的随机数n,然后 把它插到第n个空白处。
    插入方法如下:

        public int  setOneRandomNumberInRandomPosition(){
            int scores = Game2048Algorithm.getRandom2Or4();
            int blankCount = getBlankCount();
            Log.d(TAG,"blankCount:"+blankCount);
            int blankTh = 0;
            if(blankCount<=0){
                return -1;
            }else{
                blankTh = Game2048Algorithm.getRandomPosition(blankCount);
            }
            int position = mNumbers.getPositonFromBlankCountTh(blankTh);
            if (position<0){
                Log.d(TAG,"getPositonFromBlankCountTh return error");
                return -1;
            }
            Number num = mNumbers.getNumber(position/Game2048StaticControl.gamePlayMode,position%Game2048StaticControl.gamePlayMode);
            num.mScores = scores;
            num.mBeforePosition = num.mCurPosition = position;
            num.isNeedCombine = num.isNeedMove = false;
            return position;
        }

    检测游戏失败

    检测游戏的失败也有一个通用的方式:

       public boolean checkGameOver(){
            Log.d(TAG,"checkGameOver");
            for (int i = 0; i < Game2048StaticControl.gamePlayMode; i++)
            {
                for (int j = 0; j < Game2048StaticControl.gamePlayMode; j++)
                {
                    if (j != Game2048StaticControl.gamePlayMode-1 && getNumber(i,j).mScores == getNumber(i,j+1).mScores)
                        return false;
                    if (i != Game2048StaticControl.gamePlayMode-1 && getNumber(i,j).mScores == getNumber(i+1,j).mScores)
                        return false;
                }
            }
            if (mListener!=null){
                mListener.onGameOver();
            }
            return true;
        }

    算法的核心思想就是一定要对每一个数字对比它的前后左右。只要发现有相等的就认为可以继续。当然,判断的前提的空白格子的数量为0。

    展开全文
  • 新浪合并分众户外数字广告业务

    千次阅读 2008-12-26 22:03:00
    财经周刊封面:巨无霸新浪http://www.sina.com.cn 2008年12月25日 18:17 第财经周刊《第财经周刊》第43期封面 超级公司 一家原创内容受限的超级媒体,合并了一家拥有超级渠道的广告公司,然后“中国第...

    第一财经周刊封面:巨无霸新浪

    http://www.sina.com.cn  2008年12月25日 18:17  第一财经周刊
    科技时代_第一财经周刊封面:巨无霸新浪

    《第一财经周刊》第43期封面

      超级公司

      一家原创内容受限的超级媒体,合并了一家拥有超级渠道的广告公司,然后“中国第二大传媒集团”就会出现吗?新浪CEO曹国伟第一时间接受了《第一财经周刊》专访,来看看他怎么说!

      文|CBN记者 谢灵宁 黄运涛 董晓常 陆琼琼 骆轶航

      实习记者 姚晨晖

    新浪公司和分众传媒集团今天宣布双方达成协议,新浪将合并分众旗下的户外数字广告业务。根据协议,新浪将增发4700万普通股用于购买分众传媒旗下的分众楼宇电视,框架广告以及卖场广告等业务相关的资产。分众传媒将保留其互联网广告业务,影院广告业务以及传统户外广告牌业务。

      新浪CEO曹国伟表示:“这一合并的目的是为了整合中国最有实力的两大新媒体广告平台,从而为我们的客户提供更为有效的整合营销服务。我们相信本次交易将大大加强我们媒体的覆盖率和影响力,使我们成为中国广告市场不可或缺的新媒体平台。”

      分众传媒CEO谭智表示:“在过去的几年里,我们建立起来了中国最大最有影响力的户外数字广告网络,每天覆盖1.5亿城市主流消费人群。今天我们跟新浪的合并将使我们各自的优势得到充分发挥,从而更好地服务于我们的广告客户,提升我们在中国新媒体营销市场的竞争力。”

      目前这一交易已经得到双方董事会的批准不需要再经股东大会批准。如果交易条件及有关审批手续完成,本交易将在2009年上半年完成。根据双方签署的协议,新浪将增发四千七百万新浪普通股来购买分众传媒旗下的楼宇电视,框架媒体以及卖场广告业务相关的资产和业务。 这些被合并的业务在2008年前九个月占到了分众传媒全部营收的52%以及整体毛利的73%。 交易交割日后,分众传媒将马上把交易中获得的新浪股票按比例转发给分众传媒的股东。

      电话会议

      新浪和分众将于美国东部时间周一早晨10点就该项合并召开电话会议。本次电话会议接入号码:+1-866-203-3206(美国)及+1-617-213-8848 (海外);密码:59814271。电话会议在线实况转播预定于美国东部时间2008年12月22日周一早10点至11点(北京时间2008年12月22日晚11点至12点)进行。用户可在公司网站corp.sina.com在线收听会议实况。会议内容将在公司网站corp.sina.com上保留12个月。本次电话会议的实录回放将于美国东部时间2008年12月29日午夜结束。接入号码:+1-888-286-8010(美国)及+1-617-801-6888 (海外);密码:42984285。

      新浪简介

      新浪(Nasdaq GS: SINA)是一家服务于中国及全球华人社群的领先在线媒体及增值资讯服务提供商。新浪拥有多家地区性网站,以服务大中华地区与海外华人为己任,通过旗下五大业务主线:即提供网络新闻及内容服务的新浪网(SINA.com)、提供移动增值服务的新浪无线(SINA Mobile)、提供Web 2.0服务及游戏的新浪互动社区(SINA Community)、提供搜索及企业服务的新浪企业服务(SINA.net)以及提供网上购物服务的新浪电子商务(SINA E-Commerce),向广大用户提供包括地区性门户网站、移动增值服务、搜索引擎及目录索引、兴趣分类与社区建设型频道、免费及收费邮箱、博客、影音流媒体、游戏社区、分类信息、收费服务、电子商务和企业电子解决方案等在内的一系列服务。公司收入的大部分来自网络广告和移动增值服务,小部分来自搜索及其他收费服务。

      下面这件事可能是始终在追逐“想象空间”的风险资本在中国所能想到的最具想象力的事情:把分众和新浪合并在一起。这样做的结果就是,无论你坐在电脑前、进出写字楼、乘坐电梯还是在卖场里购物,你视野中的大部分广告都被同一家公司占据。

      如果一切顺利的话,这样一家超级公司将在2009年上半年诞生。12月22日晚间21时,这两家公司的董事会直接达成了协议—甚至已经“不需要再经股东大会批准”。新浪将增发4700万普通股、初步预计约合13.7亿美元的代价,合并分众传媒旗下的分众楼宇电视、框架广告、卖场广告等业务相关资产。

      12月23日,曹国伟在北京办公室接受了《第一财经周刊》记者的专访。他显得很轻松,偶尔还要兴奋地打一个响指。他说,合并并没有大家想象中突然,事实上“已经准备了很长一段时间,我跟分众很多中层都已经熟悉了”——事实上,在合并前曹已经是分众的董事了。在合并中,他自己投入精力最大的工作是平衡好两方股东、团队的利益。

      考虑到两家公司的庞大规模,这种平衡当然并不容易做到。

      分众传媒和新浪分别是中国最大的户外媒体广告商(占17%的市场份额),以及中国最大的新闻门户网站和最大的在线品牌广告商(占25%的市场份额)。两者分别与3000家和1000家品牌广告主有着长期合作。

      按易凯资本CEO王冉的算法,双方2007年的财务数据中,分众营收达到5亿美元,新浪自身也有2.46亿美元的销售额,合并后的新浪销售额将可超过人民币50亿元,成为仅次于央视的中国第二大广告平台—对一家民营公司来说,这样的成绩堪称奇迹。

      过去5年间,分众传媒在中国100多个城市安装的终端覆盖了超过7万栋楼宇、16万部电梯、4000家大中型卖场和便利店。截至2008年9月30日,分众传媒的“商务楼宇联播网”安装的液晶屏和数字框架总数量超过12万块,卖场终端联播网液晶屏包括玺诚传媒总数量超过5万个,安装的可用非数字框架超过27万个,数字框架2.9万个。在商务楼宇电视、框架平面媒体、卖场广告业务中,分众均占据着90%以上的市场份额。

      现在,所有这些都是新浪的了。

      易观国际分析师高晓虎在接受《第一财经周刊》采访时说,两者组合在一起对广告主产生的吸引力将相当强。受到经济大环境的影响,2009年各企业的广告预算肯定会不同程度地削减。削减以后,广告主会向非常优质的媒体倾斜,而对非主流媒体可能暂缓投放,甚至不投放。两个巨无霸的整合将增大对优质广告主的吸引力,无形中挤压产业链中原来跟它们处于同一级或位置较低的媒体的生存空间。

      万马堂广告公司董事长、广告业资深人士沈毅告诉《第一财经周刊》,合并双方近两年经常接触,5-6个月前开始筹划此事。他说,合并“基本是分众股东的意志”。

      2008年3月,分众传媒董事局主席江南春曾对《第一财经周刊》表示,“我几乎没看到现在谁做的是我没想到的。凡是能够做大的东西,都已经被分众做完了。”2005年,江南春实施了分众的第一次并购行动,目标是现任分众传媒CEO谭智带领的框架传媒。用江南春的话说,这次并购的结果是让自己“清楚地看到了整合的价值”,并在此后开启了江南春一系列大收购。聚众、凯威点告、ACL、好耶广告、玺诚……在每个通过并购进入的领域,分众几乎都获得了第一—至少从市场份额上看。

      可是,分众一直没有对这些收购来的业务进行有效整合。沈毅评论说,分众这几年大量收购花费了很多钱,也靠圈地迅速发展,但现在同质的企业已经收无可收,即使有这样的企业,它们的出价也非常高—它们认为反正分众有钱收购,所以分众一下子没有了扩大自己的能量。“做生意需要点线面结合,而分众只赢在了面上,点和线都不到位。”

      新生代市场监测机构副总经理兼研究总监肖明超告诉《第一财经周刊》,对目前的分众来说,业务增长无非有这样几个来源:涨价没有充足的理由,并购已经没有特别好的资源;而把广告网络延伸到下一级市场,又缺乏足够多的、成熟的商务人群。“上面这三点都不具备的话,期望分众每年的业绩保持很高的增长,其实是很不现实的。”

      此外,分众也缺钱。从设备上来说,分众目前全国的电视屏30%到40%都是第一代的产品,30%是第二代产品,要是全部更新换代需要一大笔资金。不是分众没钱换设备,可是在不盈利的状态下股东怎么可能同意花钱?

      2008年经济危机来临,许多广告主开始缩减投放。出于对分众主营业务萎缩的担心,投资者们开始慢慢看空分众。这一年,分众的市值由近80亿美元直跌到12月初的最低点10亿美元以下。

    合并之前,分众首席财务官吴明东曾预测,分众第四季度营收将较第三季度下滑10%以上,其中商务和住宅楼广告营收可能减少5%左右。

      从第三季度开始,新浪的高速增长也有了放缓的迹象。当季度新浪净利润为2200万美元,较去年同期的1720万美元增长28%,但较上一季度的2520万美元则下降了13%。

      从“内容为王”到“应用为王”再到“广告为王”,历时十年,新浪经历了一家新锐媒体公司向成熟广告公司的完整蜕变。在中国的政策管制下,新浪其实并不是一家制造信息的媒体,而是以转载传统媒体信息为主的信息大平台。但这家公司依靠自己出色的执行力,获得了远高于普通媒体的影响力。这也是分众所羡慕的。

      “内容是分众永久的痛,长期以来,内容一直是分众突破不了的瓶颈,分众曾经做了很多努力,但也没有成功。很显然,光有渠道没有内容是走不远的。”沈毅说。现在“分众总算找到了突破口。虽然没有获得传统的进入方式,但毕竟开始了”。

      2007年第三季度,新浪与谷歌开始了在AdSense广告方面的合作。这可能是新浪在广告业务方面的最后一次创新。

      对新浪来说,合并分众传媒才是创办十年之后,一次真正大举向广告公司全面转型的冒险行动。对新浪收入结构过于单一和投资方面过于稳健的指责可以就此停止了。事实上,当新浪在互联网广告上的主要对手—腾讯、搜狐、网易们开始希望客户信息同样能够覆盖户外人群的时候,它们将发现可以选择的资源已极其有限。

      但是,交易并未得到投资者的祝福。摩根大通甚至开始警告投资者不要接受这样的交易。这家看惯了华尔街大事件的公司发表报告认为,中国境内还没有媒体巨头的成功先例,况且分众此前所作的对于互联网、卖场及楼宇广告的全面布局也并未成功。

      在人员方面,新浪目前有2500名员工,分众传媒有超过3000名员工,2000人从事销售、管理工作,1000人从事资产维护工作。合并后新公司的总人数将超过5500人。

      “我们还没有整合销售团队的打算。”曹国伟在12月22日的分析师会议上声称,他的理由是新浪目前的广告销售模式更依赖代理商,分众则是直接倾向于面向客户销售。如此两家公司有更多的可能发挥协同作用。他说,初步计划是新浪与分众团队将使用不同的渠道来覆盖不同地区和不同时间的高端用户,因此渠道上也会出现不同策略。“比如可能在直销方面创造新的机会。”

      曹国伟告诉《第一财经周刊》,从现在起到明年年中交易完成,两家公司的整合准备将主要集中在业务和产品层面,第一步可能是将部分广告产品打包。而团队、渠道的整合暂时不会开始。

      “(这种整合)会进行的,但要看时机。”曹国伟说。同样,曹国伟说他注意到了两家公司拥有不同的文化,但这种整合也要“等到合适的时候”。

      对于这两家公司来说,可能的整合无非是以下几个方面:整合团队和销售渠道;整合各自客户、更有效率地为客户提供价值,以及在视频方面的整合。对于新浪来说,传统的文字信息内容受制于内容提供商,博客内容的知识产权是一个软肋,只有视频是他们自主产生的最具想像力的一块内容。想想看,如果这些内容能够在十几万块液晶屏幕上呈现出来,哪个广告主能抗拒这种诱惑?

      目前看来,新浪的整合计划中,产品和业务排在最前面,客户次之,而渠道与团队放在最后——一个最为稳重的选择。

      曹国伟认为,在半年并购完成之后,协同效应会立刻显现。他乐观地预测说,一部分在分众做投放的视频广告,甚至可以直接转到新浪平台上去。

      关于管理团队,目前的临时方案是两家公司的原有团队相对独立、各司其责。按照12月22日的纳斯达克公开资料计算,此次增发后,新浪总股本将达到10282万股,增发部分占45.71%,将全部由分众传媒的股东持有(对普通股东没有禁售期)。考虑到之前盛大持有的新浪股份近期被分众大量吸入,未来超级公司的最大股东归属尚未可知。

      “合并之后广告主是不是愿意买帐,现在还很难判断。但至少投资者是不太喜欢这项交易的。”易观国际CEO于扬说。12月22日,新浪26.5美元的开盘价格大大低于上一个交易日29.24美元的收盘价,分众也以11美元的低价开盘。到了12月23日收盘,新浪收于24.25美元,分众收于9.2美元。分别下跌了17.07%和16.21%。

      投资者主要的担忧可能来自两家大公司和两种主营业务整合的难度。摩根士丹利的报告说,新浪是中国领先的内容提供商,而分众传媒并不产生任何内容,所以整合将较为困难。简单地说就是,一家原创内容受限的超级媒体,合并了一家拥有超级渠道的广告公司,可“中国第二大传媒集团”并不会自然而然地出现—如何把两家企业文化鲜明、主营业务各异的大公司整合在一起,对任何管理者都是一个巨大的挑战。

      分众曾经想用强大的线下资源来帮助线上广告(好耶)发展,但两者整合并未收到明显成效。现在新浪把这个计划颠倒过来,企图将强大的线上广告平台与线下平台进行对接。未来怎么样现在还不是很清楚。但有一点可以肯定的是,两个公司合并如果没有有效整合,资本市场无论如何是不会投信任票的。

    展开全文
  • 牛客网答题笔记---数字游戏

    千次阅读 2016-09-03 21:32:15
    题目描述小易邀请你玩数字游戏,小易给你系列的整数。你们俩使用这些整数玩游戏。每次小易会任意说数字出来,然后你需要从这系列数字中选取部分出来让它们的和等于小易所说的数字。 例如: 如果{2,1,...

    题目描述

    小易邀请你玩一个数字游戏,小易给你一系列的整数。你们俩使用这些整数玩游戏。每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字。
    例如: 如果{2,1,2,7}是你有的一系列数,小易说的数字是11.你可以得到方案2+2+7 = 11.如果顽皮的小易想坑你,他说的数字是6,那么你没有办法拼凑出和为6 现在小易给你n个数,让你找出无法从n个数中选取部分求和的数字中的最小数。

    输入描述:

    输入第一行为数字个数n (n ≤ 20)
    第二行为n个数xi (1 ≤ xi ≤ 100000)
    

    输出描述:

    输出最小不能由n个数选取求和组成的数
    

    输入例子:

    3
    5 1 2
    

    输出例子:

    4
    

    题目分析

    1. 首先,我们假设{2,1,2,7}为例子,要求:无法从这n个数中选取某一部分数字求和的最小数,涉及到这种数组的最小或最大,最好先给数组排个序,sort之后,就是{1,2,2,7}
    2. 然后我们根据题意,找出最小的那个无法从n个数中抽数求和的值,那必然是这些所有的和中某一个的值+1
    3. 那我们就从1开始,设定out = 0,第一次判断res[0] > out+1,是不是比1还大,大的话就直接break了,因为直接1,你就没法从原数组中拿出数来构造,如果大于不大于1(因为数字都是大于0的,所以顶多也就是个1,之后继续判断)
    4. 因为前面的数判断了,(在这到题)是能构造出来的,那就加上它,out += res[0]。继续上面的判断是否当前的res[i]大于累加的out+1,如果是大于,那么说明我之前所有的数加起来(在这个[1~out+1]范围之内能被构造的数都有了,but依然)都不能赶上这个数out+1,而后面的那个数肯定也是大于这个out+1(因为当前的res[i]就大于了,所有想让后面的数作出被加的贡献是不可能的了),也就说明这是我们要找的数,那个无法从n个数中抽出某些数求和的数。

    代码

    大概理清以上的思路,代码也挺简单的:

    解法1:

    #include<vector>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    
    int main(){
        int n;
        while(cin>>n){
            vector<int> res(n);
            for(int i = 0;i < n;i++)
                cin>>res[i];
            sort(res.begin(),res.end());
            int out = 0;
            for(int i = 0;i < n;i++){
                if(res[i] > out+1)
                    break;
                out += res[i];    
            }
            cout<<out+1<<endl;
    
        }
        return 0;
    }

    python代码:

    //注:在牛客上提交的时候,一样的代码老是提示block错误,最后也不知道错在哪儿了,只能删了代码重新敲一遍,这时候好了.....
    while True:
        try:
            n=int(raw_input())
            a=map(int,raw_input().split())
            a.sort()
            out = 0 
            for i in range(n):
                if a[i] > out+1:
                    break
                out += a[i]
            print out+1
        except:
            break

    解法二.

    在讨论区看见的一种转换成背包问题的解法,代码和思路都贴在下面了,有时间可以思考下:

    //原作者:smallx
    import java.util.*;
    
    /**
     * 背包问题的一种,本质为"若num小于不可解的最小数,那么1,2,3...num都是可解的"。
     *
     * 思路如下:
     *
     * 将给定的数据集nums从大到小排序。我们要判断命题"num小于不可解的最小数"是否成立。
     * 我们将num看作背包,然后从nums中拿出一个最大的值v,如果num中能够放得下就放进去,
     * 如果放进去后刚好满了,则num可解,命题成立,如果不满继续迭代;如果v放不进去背包中了,
     * 那么背包剩下的容量构成一个更小的子问题(<num),并且如果想要命题成立,那么该子问题
     * 必定可解,并且解必定由v后边的数字序列构成(已从大到小排序)。
     */
    public class Main {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                int n = scanner.nextInt();
                int[] nums = new int[n];
                for(int i=0; i<n; i++)
                    nums[i] = scanner.nextInt();
    
                Arrays.sort(nums);
                long num = 1;
                while (true) {
                    long sum = 0;
                    for(int i=n-1; i>=0 && sum!=num; i--) {
                        if(nums[i] + sum <= num)
                            sum += nums[i];
                    }
                    if (sum != num) {
                        System.out.println(num);
                        break;
                    }
                    num++;
                }
            }
        }
    }

    补充

    1.故事缘由

    今天在一次面试的时候面试官老师聊到最后,说在我博客里挑一道题,让我给他讲明白就让我过,然后就选到了这道题。其实当时还是觉得挺有谱的,但是一上手,有点慌了,有些地方确实没有完完全全理清,导致代码写得七七八八,自己都有点模糊了,更别说给面试老师讲明白了,囧 …….

    2.题目意思

    所以回来再重新理一下这道题目的思路,必须完完全全弄明白:

    这到题目意思是:给出一些数字,然后我们找出一个数字k,使得用数组里任意的数字组合都没法拼凑成这个数字k,并且这个k要最小。
    

    我们先来找规律:
    假定:res [4] = 2,1,2,7
    我们给它排序一次得到:1,2,2,7

    我们从其中找规律:
        到下标为0的时候,1能直接构造出来,[1,1]
        到下标为1的时候,1,2,3都能构造出来,也就是[1,1+2]
        到下标为2的时候,1,2,3,4,5都能构造出来,也就是[1,1+2+2]
        到下标为3的时候, 由于前者最多只能构造到5,所以这里最多能构造到[1,5] && [7,7+5],也就是说,6以及大于12的数都构造不出来
    
        这里的6便是我们要找的k。
    

    找到这个规律,我们可以得出一个结论:
    一个排序过的数组,假设当前下标所在位为i,那么sum = res[0] + res[1] + res[2] …. + res[i] ,用其中任意的数字来组合出的数的取值范围是[ res[0] , sum ],也就是这中间的数都能被构造出来(任意几个数字加起来即可)。

    也就是说:假设前i项能构造出来的数是[res[0] , sum],也就是res[0] 到 sum 这之间的所有数字;这时候再加上res[i+1],那么就将范围扩大到了[ res[0] , sum ] 和 [ res[i+1] , res[i+1] + sum]
    如果res[i+1] <= sum,那么这两者就可以合并到一起[ res[0] , sum + res[i+1]]
    如果res[i+1] > sum ,那么说明这中间有无法构造出来的一些数,也就是在( sum , res[i+1] )之间,既然存在,那么最小的那个一定是sum + 1。

    这就得到我们想要的东西,k一定是sum+1。所以我们也就可以开始写代码了:

    #include<vector>
    #include<algorithm>
    int get_min_k(vector<int>& input){
        sort(input.begin(),input.end());
        int answer = 0;
        for(int i = 0;i < input.size();i++){
            if(input[i] > answer + 1)
                break;
            answer += input[i];
        }
        return answer+1;         //返回sum+1,得到最小的k 
    }

    3.结语

    就像今天遇到的面试官老师说的,学习应该是一个稳扎稳打的过程,应该自己有足够深刻的理解,再来写总结,而不是模模糊糊就胡乱拼凑过去的。
    往后的日子里,我会多对自己博客上写过的文章进行二次的理解和思考,来验证自己是否真正明白了某个问题。再不断写自己的补充和新的想法,学习之路还很远,走稳脚下的每一步。加油!

    展开全文
  • c++写的个简单的2048小游戏

    万次阅读 多人点赞 2016-08-12 21:27:25
    嗯,上周进行培训的时候老师布置了...数字消除合并 判定游戏结束 游戏主体: 因为用C++写的,所以干脆用了类,不过其实不用的话也没什么关系。。。棋盘用了个二维数组,m是棋盘规格,随便定多大无所谓,一般是4。cl
  • 我花了一夜用数据结构给女朋友写个H5走迷宫游戏

    万次阅读 多人点赞 2019-09-10 23:27:18
    起因 又到深夜了,我按照以往在csdn和公众号写着数据结构!...而我答道:能干事情多了,比如写个迷宫小游戏啥的! 当我码完字准备睡觉时:写不好别睡觉! 分析 如果用数据结构与算法造出东西来呢? ...
  • 数字游戏 NOIP2003TG/codevs1085 ...游戏是这样的,在你面前有圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到个数k。游戏的要求
  • 使用Android编写个2048小游戏

    千次阅读 2017-08-23 14:50:53
    2048小游戏还是很简单...数字相加有一定的顺序,方向上靠前的数字先与相邻的数字相加,数字每次滑动只进行次相加,比如 向左滑 2,2,2,2滑动之后是4,4而不是2,4,2或者8 - 数字之间的计算只在滑动所在的行或
  • C#游戏编程之创建个简单的卷轴射击游戏

    万次阅读 多人点赞 2013-01-14 09:05:10
    前几天很多朋友要求贴篇有关C#游戏开发的实例。本篇文章是创建个简单的卷轴射击游戏开发实例,内容比较完整,分享给大家,不过篇幅有些长,可以慢慢看哈!本文除CSDN博客外,任何媒体和出版物禁止转载和使用。 ...
  •  作为个真正合格的数字IC设计工程师,你永远都需要去不断学习更加先进的知识和技术。因此,这里列出来的技能永远都不会是完整的。我尽量每年都对这个列表进行次更新。如果你觉得这个清单不全面,可以在本
  • 导语 感觉好久没和大家一起写小游戏玩了,今天...“使用方向键移动方块,两个数字相同的方块撞在一起后,将会合并数字是原来两倍的新方块。游戏的时候尽可能多地合并这些数字方块就行了。” 大概了解了游戏规则之
  • 数字IC小白起步()

    千次阅读 多人点赞 2018-09-27 10:49:16
    刚毕业的时候,我年少轻狂,以为自己已经可以独当一面,庙堂之上所学已经足以应付业界需要。...作为个真正合格的数字IC设计工程师,你永远都需要去不断学习更加先进的知识和技术。因此,这里列出来...
  • 小白2048小游戏速成教程(python不到100行)

    万次阅读 多人点赞 2018-08-14 13:05:07
    规则与效果 2048游戏共有16个格子,初始时会有两个格子上安放了两个数字2,每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在...《2048》,是一款益智小游戏...
  • 个简单的c++小游戏——2048

    千次阅读 2020-11-27 20:17:02
    个简单的c++小游戏——2048 代码 话不多说,代码如下: #include<iostream> #include<vector> #include<ctime> #include<cstdlib> using namespace std; class Game_2048 { public: Game...
  • 前些天发现了个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:点击跳转 2048大家都玩过,也有很多人考虑过,最高到底能玩到多大的数字。 也有的人会问,最高能玩到多大的分数,但是这个明显...
  • Android开发之游戏2048代码(

    万次阅读 2019-02-26 17:32:48
    这次是个简单的2048游戏,设计比较粗糙,还可以往上面添加音乐、表情之类功能的代码, 、 先来看看游戏的最终效果: 游戏图标的设计: 二、代码的主要设计: 三、关键代码部分 Card部分: 类Card继承...
  • 个2048游戏的第二版

    千次阅读 2014-03-18 23:10:37
    这是自己实现的网上个很火的游戏《2048》,虽说创意是抄袭人家的吧,但是其中也有不少难点,也小小的费了一番功夫。 首先就是cocos2d-x引擎使用的还不狠熟练,好多地方本来个函数就解决了的,结果自己查来查去...
  • 2048游戏规则简单,一共个6个格子,可以4个方向滑动,相同的数字相遇就合并数字翻倍,数字清0,清0的腾出个格子。 当没有空白格子,用来出现新的数字时,游戏结束。 分析2018游戏数字移动规则,...
  • 最近在实现个API,其中有部分功能是需要从Mongodb中取出个由Date对象组成的数组,然后将客户端传过来的unixtime合并到该数组中,并且去重复。 比如,假设从mongodb中取回来的数据中有个叫做gaming的项,...
  • C++ 控制台版 2048小游戏

    千次阅读 2015-08-12 19:09:15
    开始的时候空格中会出现两个数字(只能为2或者4),用户可以选择上下左右键进行移动,数字们整体沿着方向移动,中间不存在空格,如果相邻的两个数字相等,那么合并至沿着方向的后一个,更新最大值,总分数加上新出现...
  • 本文基于Blender 2.8正式版 在雕刻过程中经常会新建一些物体然后...即使在缝隙处用Clay Strips笔刷涂上厚厚的层,也很容易穿帮。 解决方法:使用自带插件BoolTool中的Union无缝合并可以一步到位! 1. 激活BoolT...
  • 将整个界面抽象为个4*4的二维矩阵,矩阵存储当前格的数字,若为空则置0。 随机在空白区域产生个随机数,随着得分的不断增加,产生大数字的概率逐渐增大。 根据按键输入,读入方向键。并调用合并函数进行合并。...
  • 动态规划之石子合并

    千次阅读 2010-02-19 16:07:00
    石子游戏有两种:种是博弈方面;另种是合并规划方面的。前者需要太厉害的判断,而且要用到很多数学方面的结论,所以俺不会。这里主要探讨一下石子归并方面的问题与算法。. 简单的石子合并问题类似于背包问题:...
  • 使用c实现2048小游戏
  • 2048小游戏总结

    千次阅读 2017-09-13 12:28:31
    2048游戏的规则:一共有16个方格,可以4个方向滑动,相同的数字相遇就会合并数字翻倍,另数字清零,清零的数字会腾出个空格。当没有空白格子出现时,游戏结束。2. 基本说明: 我把16个数字放在个二维...
  • 3D游戏引擎剖析

    千次阅读 2015-02-21 10:11:09
    DOOM不只是一款伟大的游戏,它同时也开创了一种新的游戏编程模式: 游戏 "引擎"。 这种模块化,可伸缩和扩展的设计观念可以让游戏玩家和程序设计者深入到游戏核心,用新的模型,场景和声音创造新的游戏, 或向已有的...
  • 用js实现2048小游戏

    千次阅读 2018-02-12 09:48:41
    用js实现2048小游戏笔记仓库:https://github.com/nnngu/LearningNotes 1、游戏简介2048是一款休闲益智类的数字叠加小游戏。(文末给出源代码和演示地址)2、游戏玩法在 4*4 的16宫格中,您可以选择上、下、左、...
  • DOOM不只是一款伟大的游戏,它同时也开创了一种新的游戏编程模式: 游戏 "引擎"。 这种模块化,可伸缩和扩展的设计观念可以让游戏玩家和程序设计者深入到游戏核心,用新的模型,场景和声音创造新的游戏, 或向已有的...
  • 、引言有天中午正好看到有同事在玩消消乐,也即是出名的三消类游戏,顿时引发了我想要探索实现个三消类游戏的兴趣。花了两天的探索,终于有了个半成品,这篇博客就将我这两天的思考过程记录下来,以飨读者。上...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,421
精华内容 10,968
关键字:

一款数字合并的游戏