實驗項目名稱:紙牌遊戲
實驗目的:
設計一個簡單的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。目前只初步設計了一下新的紙牌遊戲。