[OOD设计] - Black Jack Game (21点)

题目描述&链接

21点游戏OOD设计:设计一个可以多人在线的21点游戏,一个游戏室会有一个荷官(发牌员),多名玩家,一条牌组。每一名玩家用手里的牌与发牌员比大小,谁越接近21点,谁就算赢。如果你的牌是一个Ace + 10的话,那么你就是Black Jack也就是游戏中最大牌。

题目结构设计梳理

作为一个初学者,一开始看这道题着实是没有什么思路,不知道从哪里下手,而且看了网上一些设计,写的确实很好,但是我认为功能过于完善,我并不认为在面试20分钟的时间内,能够做到那种完美的设计。我这篇文章主要的想法一个是总结我自己作为新手在接触OOD设计题目的理解,以及根据网上资源教程和自己的理解,整理了我觉得最为重要的几个核心部分。我认为会更便于像我一样的初学者进行学习。

作为OOD设计问题,归根到底无非就是类的设计以及类之间调用关系设计,另外代码结构上最后运用一些可以使用上的设计模式(Factory, Strategy等等)进行结构优化。

OOD设计首先考虑时间,地点,人物这三要素,大致会有下列这些元素:

  • 时间 - 21点游戏里感觉没有什么需要考虑时间节点(如果开局,退出不算的话)
  • 地点 - 应该有一个游戏室,这个游戏室可以加入发牌员,玩家,并且所有结算筹码,判断游戏输赢,都应该在游戏室内完成
  • 人物 - 发牌员(负责跟玩家比大小,初始化牌组) , 玩家(拿牌,改筹码,这局游戏跟还是不跟)

整体流程可以设计成如下图所示:

1. 卡牌类

首先我们要有游戏媒介 - 纸牌,21点游戏并不需要考虑花色,纸牌唯一需要的信息就是数组(A-K) 也就是 1-13.

class Card {

    private int value;
    
    public Card(int v) {
        value = v;
    }

    public int getValue() {
        return value;
    }
}

接下来,每一个玩家和发牌员都有手牌,由于手牌类我们也想加入一些功能包括:查看当前手牌合计分数,加入手牌等,所以可以将手牌建立成一个类。

class Hand {
    private List<Card> hands;
    
    public Hand() {
        hands = new ArrayList<>();
    }

    // 添加手牌
    public void addHand(Card card) {
        hands.add(card);
    }

    // 清空手牌,准备下一局或者退出游戏
    public void clear() {
        hands = new ArrayList<>();
    }

    // 找到当前手牌<=21的最大分数
    public int getBestValue() {
        // 计算出当前手牌最好的分数,由于Ace可以1,11 所以手牌分数存在多种可能
        // 首先枚举处手牌可以组成的所有分数
        List<Integer> val = new ArrayList<>();
        int aceCount = 0;
        int ans = 0;

        for(Card c: hands) {
            if(c.getValue()==1) {
                aceCount++;
            } else if(c.getValue>=10) {
                ans+=10;
            } else {
                ans+=c.getValue();
            }
        }

        for(int i=0; i<=aceCount; i++) {
            int one = i;
            int eleven = aceCount-i;
            
            val.add(ans + one*1 + eleven*11);
        }

        int res = -1; // if ans>21 -> return -1
        for(int v: val) {
            if(v<=21 && res<v) res = v;
        }
        
        return res;
        
    }
}

2. 人物类

首先是玩家类,玩家主要的功能就是抽牌,调整筹码,选择是否继续进行游戏。

class Player {
    Hand hands;
    int totalBets;
    int bets;
    boolean stopDealing;

    public Player() {
        hands = new Hand();
        totalBets = 1000;
        stopDealing = true;
    }
    
    public void drawCard(Card card) {
        hands.addCard(card);
    }

    public void clear() {
        hands.clear();
    }

    public Hand getHand() {
        return hands;
    }


    public void addBets(int v) {
        // 筹码不合理,退出
        if(v<=0 || v>totalBets-bets) return;
        
        // 如果筹码合理,就添加
        bets+=v;
    }

    public void addTotalBets(int v) {
        if(v<=0) return;

        totalBets+=v;
    }

    public int getBets() {
        return bets;
    }

    public int getTotalBets() {   
        return totalBets;
    }

    public void stopDealing() {
        stopDealing = false;
    }

    public boolean isDealing() {
        return stopDealing;
    }

    
}

然后是发牌员,发牌员没有Stop Dealing的选择,以及发牌员不需要出筹码。

class Dealer{
    Hand hands;
    int totalBets;   
    // 发牌员不需要有筹码,如果赢了就收钱,输了就结账即可 

    public Dealer() {
        hands = new Hand();
        totalBets = 10000;
    }
    
    public void drawCard(Card card) {
        hands.addCard(card);
    }

    public void clear() {
        hands.clear();
    }


    public void addTotalBets(int v) {
        if(v<=0) return;

        totalBets+=v;
    }

    public void setTotalBets(int v) {
        totalBets = v;
    }

    public int getTotalBets() {   
        return totalBets;
    }

    public boolean isLarger(Player p) {
        Hand phand = p.getHand()
        return hands.getBestValue()>=phand.getBestValue();
    } 

    
}

3. 游戏室类

该OOD中最主要的设计,其中包含整个游戏规则流程的逻辑,开牌发牌逻辑,添加发牌员和玩家,核心功能都在这个大类中

class Game {
    List<Card> cards;
    int nextIdx;
    Dealer dealer;
    List<Player> playes;
    
    public Game() {
        players = new ArrayList<>();
    }

    public void addPlayer(Player p) {
        if(players.size()<5) players.add(p);
    }

    public void addDealer(Dealer d) {
        dealer = d;
    }

    public void initGame() {
        if(Dealer==null || players.size()<=0) return;
        
        // 初始化牌组,并且进行洗牌shuffle
        // 四个花色 + 13张牌
        for(int i=0; i<4; i++) {
            for(int j=1; i<=13; j++) {
                Card c = new Card(j);
                cards.add(c);
            }
        }

        // shuffle的逻辑就是随机选择在当前位置i到end之间的某个位置j将i,j进行交换
        for(int i=0; i<cards.size(); i++) {
            int j = Math.random()*(cards.size()-i) + i;
            
            Card a = cards.get(i);
            Card b = cards.get(j);
            
            //交换位置
            cards.set(i, b.getValue());
            cards.set(j, a.getValue());
        }
    }

    public void sendCard() {
        if(cards.size()>nextIdx) return cards.get(nextIdx++);
    }

    
    public void compareResult() {
        // 循环计算player结果
        for(Player i: players) {
            if(i.stopDealing()) continue;
            if(dealer.largerThan(i)) {
                dealer.setBets(i.getBets());
                if(i.getTotalBets()-i.getBets()==0) {
                    i.setStopDealing(true);
                }
                // 
                i.setTotalBets(i.getTotalBets() - i.getBets());
            } else {
                // player wins
                if(dealer.getBets()<i.getBets()) {
                    dealer.setBets(dealer.getBets() + 3*i.getBets()); //如果dealer筹码不够加筹码
                }
                i.setTotalBets(i.getTotalBets() + i.getBets()); // 更新总筹码
                
            }
            i.setBets(0);
        }
    }
}

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值