程序功能阐述如下:
由电脑随机从扑克牌堆中抽取前两张牌,一张属于用户,一张属于电脑,比较其大小:
大小规则:数字规则:
2<3<4<5<6<7<8<9<10 花色规则: 方块<梅花<红桃<黑桃 比较规则:数字优先,同数字,花色优先。 项目截图: 针对这个项目,我们先把思路做一下整理: 在这个项目中用到了数据,我们先从数据入手,进行数据的分析: 对于发牌比大小,数据就是牌。 单纯说牌,还有一个前提是比较大小.那么在数据进行设计的时候,比较简单和直接的设计就是: 按照牌大小顺序排好。 我们先看我们定的规则: 1、黑桃>红桃>梅花>方块 2、无论任何花色2<3<4<5<6<7<8<9<10 3、大王最大,其次是小王 我们在排这个列表的时候可以根据方块2、梅花2、红桃2、黑桃2、方块3、….黑桃A 、小王、大王这个顺序来组织和排序数据。 排完以后,诸如:方块2、梅花2、红桃2 、黑桃2等内容都可以是一个String类型,根据String类型的特点:一个String就是一个char数组,在堆内存里有一定的空间存储。54张牌就要有54个堆内存空间来存储。在对这个堆内存空间操作的过程中,也对字符串进行处理,而String的处理其实就是在String会进行数组复制,进而浪费内存。 针对这一问题,我们可以采用技术:读写分离。 把字符串做为一个变量也好,静态也罢,只是储存着牌的信息,也不能比较大小,真正比较大小还需要我们的整型数据,我们能不能把牌的信息和整型数据结合起来,一一对应,一个整型数据对应一个牌的信息,当我们使用这个牌的时候,再进行对应关系的映射,我们的操作只针对这个整型的一个集合。这就构成了我们的读写分离,读的是牌的信息和整型数的对应关系,写或者说操作的都是整型的列表或集合。 那么整型库的类型可以定义成ArrayList 整型库与牌信息的对应关系我们应用Map集合: Map 一副牌理好以后,我们需要洗牌,也就是打乱牌。 打乱牌始终对ArrayList中的Integer操作,取得这张牌也可以对ArrayList的Integer操作,比较也是对ArrayList的Integer操作。这一期间都是对Integer操作,直到需要对应关系的时候我们直接找那个key对应的String映射。 这样数据的思路出来了: 也就是我们可以定义一个数据类,比如这个类叫PuKe。这里至少先有两个成员变量: 一个list列表,里面存0-----53的整数。 一个Map 集合里面存0—53和方块2----大王的对应关系 初始化的时候可以对这两个成员变量进行赋值: 就是把花色牌和比较的数字一一对应。 这样我们得到的代码可以是: 先定义两个成员变量: 再进行初始化: 初始化以后进行赋值: 思路:我们可以把花色和点数进行循环匹配出牌面来,用一个标志整数位的值来进行累加计算整型值。 这样把数据放在列表中就变成以下形式: 这个数据里缺少大王和小王,我们再把大王和小王对应的数值也放进去。 在数据PuKe类的构造函数里有了以下代码以后,我们可以用map的keySet遍历方法: 可以在主类中调用一下,看一下结果是不是我们想象中的样子: 放在Map集合中的结果已经完成,也是我们期望的顺序。下面我们要进行的就是集合中的数据打散,进行洗牌的操作。 洗牌实际上就是把纸牌集合随机进行两两交换,进行若干次,就可以把集合中的元素顺序弄乱。由于我们采用读写分离,实际上我们打乱的是Integer整型数据的集合。 实际的代码实现如下: 当然,由于collections工具类的引用,这段程序也可以用工具类实现: 把集合打散后,相当于我们已经完成了扑克牌集合的准备。下一步我们要做的事就是能够从牌顶拿出一张牌给玩家,再拿出一张牌给电脑,然后比较大小。 从牌库顶拿一张牌我们需要写方法get 但要注意的是,我们每次拿的牌是不一样的,才能保证电脑和玩家的牌不同,我们就得需要定义一个index变量,这个变量存放的就是牌库的索引值,每次拿牌索引值增加,就可以保证每次拿到不同的牌,不过前提是这个值不会因为程序的初始化而变化,所以用静态变量来定义这个index值。 这样代码变成: 这样我们就实现了一个拿牌的过程。 不过我们拿到的是数字,在外面调用后,一定会进行数字对应的牌面的转换,我们就需要把map这个存放牌值的集合进行封装后的getter,setter方法,供外面调用后把数字转成相对应的牌面。 整个数据类我们已经完成了,下面我们就要进行游戏角色类的设置了。 对于游戏角色类,我们不难分析,由电脑和用户端同时拿一张牌,然后比较大小,从这个要求中,就会明白,我们需要电脑类、用户类,由于这两个类都需要从数据牌库类中取牌,方法也是一样的,所以我们可以找到他们的共同父类,我们暂且叫选手类。 在选手类中我们不难分析,他们应该有成员变量就是他们各自的名字,我们可以简单处理,初始化的时候就把名字进行赋值,然后有一个方法从数据牌库中拿牌,拿到牌后转化成我们需要的牌面信息。 以上是选手类的相关代码: 电脑类和用户类直接继承于选手类即可,不用做方法的重写,但从继承的相关理论中我们知道,构造器不能继承过来,我们在子类继承中把构造器用super关键字来继承过来,这样子类就有自己的名字了,再进行比较牌面。 于是电脑类Computer就为: 用户类就成为了: 参与的角度类建立成功后,我们创建操作类,就是游戏的整个运行过程类: 一般的游戏过程类都包括:游戏初始化方法,游戏运行方法,游戏判断方法和游戏结束方法, 于是这个运行过程类就包括: Init() 方法 game()方法 judge()方法 gameover()方法 而对于这个游戏,init()可以实现发牌,发完牌直接比较,judge()方法,最后再gameover方法,不需要game()方法 我们可以写一个运行过程类的接口,让开发者都遵循我们统一的命名标准和规范。 接下来我们定义一个操作类实现这个接口 在这个操作类中,我们只需要实现init()、judge()和gameover方法即可。 Init()方法中实例牌库、电脑、用户,然后电脑和用户从牌库中取牌。 这里定义成员变量的Computer、Player、Puke 三个变量的原因是由于这三个成员变量在init()方法、judge()方法和gameover方法中都要调用,所以定义一个全局的成员变量。 new Computer和new Player的过程中直接给这三个成员变量的name赋值,即参与游戏的两个角色,然后new PuKe()是把扑克牌产生,并调用PuKe中的shuffle方法打乱。再由Computer成员变量comp调用其父类的方法fetch一张牌,再由Player成员变量player调用其父类的方法fetch一张牌,然后执行judge(); 这是上面代码的相关逻辑。 然后我们看judge()的完成。 在judge方法中,我们明显需要得到一个量,这个量就是扑克牌对应的整型值,而这个值我们也放在了扑克牌类中的ArrayList列表中,我们即然要取这个值,就需要知道电脑类、玩家类中fetch方法中获取的牌对应的整型值是多少,我们没有定义这个获取方法,因此需要把电脑类和玩家类的父类再增加一个成员变量,这个变量就是key,我们通过fetch()方法获取了牌之后,还要把key值存储一下,然后定义getter方法让外界可以调用,这样judge就可以进行key值的比较了,继而来决定牌的大小。 先看改写的选手类: 增加key值: 定义getter方法,外界不需要对其设置,所以不用设置setter。 在fetch的时候我们把key 值也保存下来。 这样选手类的key值设置完成后,电脑类和用户类的key值也设置了。 下面我们完成操作类的judge()方法,代码如下: 看逻辑也非常简单。 对比电脑的key值与玩家的key 值,大了话“玩家输了”,小的话“玩家赢了”。 最后调用gameover()方法. gameover()方法也非常简单,就是输出一句“游戏结束,欢迎使用“。 这个游戏程序的最后,我们需要定义主类来调用,我们叫MainLei,这个名称可以自己定义。在其中只要初始化一下游戏操作类,即CaoZuo类实例化和调用其init方法即可: 至此,整个程序完成。我们可以看一下整体的程序目录结构,养成良好的结构习惯。