实验项目名称:纸牌游戏
实验目的:
设计一个简单的CardGames程序,运用面向对象封装、继承、抽象类、抽象方法、多态、动态绑定等概念。
实验目标与要求:
参考windows的纸牌游戏
使用语言:java
实验内容:
单人纸牌游戏,牌桌上有7个堆共28张牌,第一堆1张牌,第二堆2张,。。。第7堆7张,每一堆的第一张牌朝上,其他朝下。牌桌上还有4个suitpiles,一个deck card堆和一个discard card堆,布局如下(参考windows的纸牌游戏)
程序的总体设计:
利用CRC (Class,Responsibility,and Collaboration) 方法来设计,由于实验是自己一人独立完成,在设计时没有多人参与crc设计,自己扮演了多个角色。
UML图:
主要代码及其说明:
1. 发配算法:
位于solitaire.game.Game类里面
- static{
- //初始化纸牌
- allCard = new ArrayList<Card>();
- for (int i = 0; i < 4; i++)
- for (int j = 0; j <= 12; j++)
- allCard.add(new Card(j, i));
- Random generator = new Random();
- for (int i = 0; i < 52; i++) {
- int j = Math.abs(generator.nextInt() % 52);
- // swap the two card values
- Card temp = allCard.get(i);
- allCard.set(i, allCard.get(j));
- allCard.set(j, temp);
- }
- //初始化牌堆
- allPiles = new CardPile[13];
- suitPile = new SuitPile[4];
- tablePile = new TablePile[7];
- // then fill them in
- allPiles[0] = deckPile = new DeckPile(200, 40);
- allPiles[1] = discardPile = new DiscardPile(200+Card.width+50, 40);
- for (int i = 0; i < 4; i++)
- allPiles[2+i] = suitPile[i] =
- new SuitPile(200+Card.width+50 + Card.width + 150 + (40+Card.width) * i, 40);
- for (int i = 0; i < 7; i++)
- allPiles[6+i] = tablePile[i] =
- new TablePile(200 + (50+Card.width) * i, 40 + Card.height + 40, i);
- for (int i = 0; i < 7; i++){
- ArrayList<Card> al = new ArrayList<Card>();
- for(int j = 0;j < tablePile[i].getCardNum();j++){
- al.add(allCard.remove(allCard.size()-1));
- }
- tablePile[i].addCard(al);
- tablePile[i].setCardNum(tablePile[i].getNotFlipNum()+1);
- //System.out.println(tablePile[i].getCardNum());
- tablePile[i].top().setFaceup(true);
- }
- int rest = allCard.size();
- for(int i = 0;i < rest;i++ ){
- deckPile.addCard(allCard.remove(allCard.size()-1));
- //System.out.println( i);
- }
- moveCard = new MoveCardPile();
- }
说明:该算法先创建52张纸牌(Card)对象,并放置allCard(ArrayList)中,然后模拟现实当中的洗牌操作,主要是利用java的Random来打乱allCard里牌的排列顺序,然后初始化各个牌堆类: deckPile, discardPile, tablePile, suitPile, moveCard。此外还建了一个数组allPiles(CardPile[]),用于存储所有的牌堆类。最后将allCard中的纸牌牌(Card)对象分发至各个堆里。
2. select方法(solitaire.pile. CardPile):
传给该函数坐标用于判断点中该牌堆中的某张纸牌(还有个include方法用于判断是否点中牌堆),其中因为TablePile要支持选中多张纸牌,要改写CardPile的方法。
CardPile的select方法:
- public int select (int tx, int ty) {
- if(includes(tx,ty)){
- if(isEmpty())
- return -2;
- else
- return thePile.size() - 1;
- }
- else
- return -1;
- }
TablePile的select方法:
- public int select(int tx, int ty) {
- // TODO Auto-generated method stub
- if(!(isEmpty())){
- int beginX,beginY,endX,endY;
- //System.out.println(notFlipNum+" "+cardNum);
- beginX = x ;
- beginY = y + unFlipCardSeparation * notFlipNum;
- endX = x + Card.width;
- endY = beginY + unFlipCardSeparation * notFlipNum + separation * (thePile.size() - 1 - notFlipNum) + Card.height;
- boolean flip_include = beginX <= tx && tx <= endX && beginY <= ty && ty <= endY;
- //System.out.println(beginY+" "+endY);
- if(flip_include){
- int c = (ty - beginY)/separation + notFlipNum;
- if(c >= thePile.size()){
- c = thePile.size() - 1;
- }
- return c;//从零开始
- }
- else
- return -1;
- }
- else
- return -1;
- }
3. isCanAdd方法
用于判断某张纸牌是否可以放于SuitePile或TablePile
CardPile类:
- public boolean isCanAdd(Card card){
- return false;
- }
SuitePile类:
- public boolean isCanAdd(Card card) {
- // TODO Auto-generated method stub
- if (isEmpty())
- return card.getNum() == 0;
- Card toptopCard = top();
- return (card.getType() == topCard.getType()) &&
- (card.getNum() == topCard.getNum() + 1);
- }
Tablepile类:
- public boolean isCanAdd(Card card){
- // TODO Auto-generated method stub
- if ( isEmpty())
- return card.getNum() == 12;
- Card toptopCard = top();
- return (card.getColor() != topCard.getColor()) &&
- (card.getNum() == topCard.getNum()-1 );
- }
4. refreshTablePile
用于刷新Tablepile,主要用于处理一走TablePile中的纸牌时剩余纸牌都是背面的时候,将最上面的纸牌翻转过来。
- public static void refreshTablePile(){
- // System.out.println("refreshTablePile");
- for(int i=0;i<7;i++){
- if(tablePile[i].top() != null)
- if(!(tablePile[i].top().isFaceup())){
- tablePile[i].top().setFaceup(true);
- tablePile[i].setNotFlipNum(tablePile[i].getNotFlipNum()-1);
- }
- }
- }
实验截图:
实验心得:
结合课堂上的学习,利用了面向对象的思想:继承,多态(改写,多台变量),范型等等,对面向对象思想有了更好的把握。
后来学习了设计原则,我觉得还得自己写的代码违背了许多原则,例如Game里面的CardPile不是抽象的,应将其声明为抽象的以便让其他类依赖抽象。此外我觉得可利用mvc架构来设计纸牌游戏,利用观察者模式来处理model的更改来更新view,用组成模式来设计view,利用策略模式来设计control。目前只初步设计了一下新的纸牌游戏。