日麻役种计算器的java雏形实现

本项目实现了一个简易的日麻役种计算器,技术栈为Java Swing。包括前端的选牌,看你选的牌,根据你选的牌计算等部分。

主页面部分:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.Map;
public class MainJ extends JFrame {
    private Graphics g;
    private JLabel pointsLabel;
    private static final String IMAGE_DIR = "images/"; // 图片存放目录
    private static final String[] TILE_NAMES = {
            // 万子
            "wan1", "wan2", "wan3", "wan4", "wan5", "wan5bao", "wan6", "wan7", "wan8", "wan9",
            // 筒子
            "tong1", "tong2", "tong3", "tong4", "tong5", "tong5bao", "tong6", "tong7", "tong8", "tong9",
            // 索子
            "suo1", "suo2", "suo3", "suo4", "suo5", "suo5bao", "suo6", "suo7", "suo8", "suo9",
            // 字牌和特殊牌
            "special_dong", "special_nan", "special_xi", "special_north",
            "special_zhong", "special_fa", "special_bai"
    };
    private MahjongTile[] mainList = new MahjongTile[14];
    private int mainListIndex = 0;
    private int finalScore = 0;
    private static final Map<String, MahjongTile> TILE_MAP = new HashMap<>();

    static {
        TILE_MAP.put("wan1", MahjongTile.WAN_1);
        TILE_MAP.put("wan2", MahjongTile.WAN_2);
        TILE_MAP.put("wan3", MahjongTile.WAN_3);
        TILE_MAP.put("wan4", MahjongTile.WAN_4);
        TILE_MAP.put("wan5", MahjongTile.WAN_5);
        TILE_MAP.put("wan5bao", MahjongTile.WAN_5BAO);
        TILE_MAP.put("wan6", MahjongTile.WAN_6);
        TILE_MAP.put("wan7", MahjongTile.WAN_7);
        TILE_MAP.put("wan8", MahjongTile.WAN_8);
        TILE_MAP.put("wan9", MahjongTile.WAN_9);

        // 筒子
        TILE_MAP.put("tong1", MahjongTile.TONG_1);
        TILE_MAP.put("tong2", MahjongTile.TONG_2);
        TILE_MAP.put("tong3", MahjongTile.TONG_3);
        TILE_MAP.put("tong4", MahjongTile.TONG_4);
        TILE_MAP.put("tong5", MahjongTile.TONG_5);
        TILE_MAP.put("tong5bao", MahjongTile.TONG_5BAO);
        TILE_MAP.put("tong6", MahjongTile.TONG_6);
        TILE_MAP.put("tong7", MahjongTile.TONG_7);
        TILE_MAP.put("tong8", MahjongTile.TONG_8);
        TILE_MAP.put("tong9", MahjongTile.TONG_9);

        // 索子
        TILE_MAP.put("suo1", MahjongTile.SUO_1);
        TILE_MAP.put("suo2", MahjongTile.SUO_2);
        TILE_MAP.put("suo3", MahjongTile.SUO_3);
        TILE_MAP.put("suo4", MahjongTile.SUO_4);
        TILE_MAP.put("suo5", MahjongTile.SUO_5);
        TILE_MAP.put("suo5bao", MahjongTile.SUO_5BAO);
        TILE_MAP.put("suo6", MahjongTile.SUO_6);
        TILE_MAP.put("suo7", MahjongTile.SUO_7);
        TILE_MAP.put("suo8", MahjongTile.SUO_8);
        TILE_MAP.put("suo9", MahjongTile.SUO_9);

        // 字牌和特殊牌
        TILE_MAP.put("special_dong", MahjongTile.DONG);
        TILE_MAP.put("special_nan", MahjongTile.NAN);
        TILE_MAP.put("special_xi", MahjongTile.XI);
        TILE_MAP.put("special_north", MahjongTile.BEI);
        TILE_MAP.put("special_zhong", MahjongTile.ZHONG);
        TILE_MAP.put("special_fa", MahjongTile.FA);
        TILE_MAP.put("special_bai", MahjongTile.BAI);
    }

    public MainJ() {
        JPanel backgroundPanel = new JPanel(new BorderLayout()) {
            private Image backgroundImage;

            {
                try {
                    // 从文件或资源路径加载图片
                    backgroundImage = ImageIO.read(getClass().getResource(IMAGE_DIR+"face.png"));
                } catch (IOException e) {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(this, "无法加载背景图片!");
                }
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (backgroundImage != null) {
                    // 绘制图片(自适应面板大小)
                    g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
                }
            }
        };
        setTitle("役种/番数计算器");
        setExtendedState(JFrame.MAXIMIZED_BOTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        GridLayout layout = new GridLayout(4, 10);
        JPanel cards = new JPanel(layout);
        cards.setBackground(Color.gray);
        JPanel alreadies = new JPanel(new GridLayout(1,14));
        alreadies.setPreferredSize(new Dimension(getWidth(), 160));
        alreadies.setBackground(Color.ORANGE);
        cards.setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();

        pointsLabel = new JLabel("最终点数:0");
        pointsLabel.setFont(new Font("仿宋", Font.BOLD, 55));
        pointsLabel.setForeground(Color.MAGENTA);
        pointsLabel.setBounds(5, 2, 200, 300);
        pointsLabel.setVisible(true);
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(5, 2, 5, 2); // 组件之间的间距
        int index = 0;
        for (String tileName : TILE_NAMES) {
            ImageIcon imgIcon = new ImageIcon(Objects.requireNonNull(getClass().getResource(IMAGE_DIR + tileName + ".png")));
            if (imgIcon.getImageLoadStatus() == MediaTracker.COMPLETE) {
                JLabel label = new JLabel(imgIcon);
                label.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        MahjongTile tile = TILE_MAP.get(tileName);
                        if (tile != null && mainListIndex < mainList.length) {
                            ImageIcon tileIcon = new ImageIcon(getClass().getResource(IMAGE_DIR + tileName + ".png"));
                            JLabel selectedLabel = new JLabel(tileIcon);

                            // 添加到已选牌区域
                            alreadies.add(selectedLabel);
                            alreadies.revalidate();  // 刷新布局
                            alreadies.repaint();

                            // 更新数据存储
                            mainList[mainListIndex++] = tile;
                            System.out.println("已选牌: " + tile);
                            if (mainListIndex == mainList.length+1) {
                                boolean isHule = true; // 假设已经和牌
                                MahBooleans mahBooleans = new MahBooleans(); // 根据实际情况设置参数
                                BackendCalc bc = new BackendCalc(mainList, isHule, mahBooleans);
                                finalScore = bc.getFinalScore();
                                pointsLabel.setText("最终点数:" + finalScore); // 更新点数显示
                                pointsLabel.setVisible(true);
                            }

                        }
                    }
                });
                gbc.gridx = index % 10; // 设置x坐标
                gbc.gridy = index / 10; // 设置y坐标
                cards.add(label, gbc);
                index++;
            } else {
                System.err.println("Failed to load image: " + tileName);
            }
        }
        backgroundPanel.add(cards, BorderLayout.SOUTH);
        backgroundPanel.add(alreadies,BorderLayout.NORTH);
add(backgroundPanel);
        backgroundPanel.add(pointsLabel);
        MahjongListener mahjongListener = new MahjongListener();
        cards.addMouseListener(mahjongListener);
        setContentPane(backgroundPanel);



        setVisible(true);
        System.out.println(mainList);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new MainJ());
    }
}

可以看到,里面主要有诸如主页面牌的位置,54张牌的可供选择的点击地点,和已经被选择的牌的显示(当然还有一个答案显示和背景Panel)
enum牌堆:

public enum MahjongTile {
    // 万(Wan)
    WAN_1(Suit.WAN, 1, false, 0),
    WAN_2(Suit.WAN, 2, false, 1),
    WAN_3(Suit.WAN, 3, false, 2),
    WAN_4(Suit.WAN, 4, false, 3),
    WAN_5(Suit.WAN, 5, false, 4),
    WAN_5BAO(Suit.WAN, 5, true, 5),
    WAN_6(Suit.WAN, 6, false, 6),
    WAN_7(Suit.WAN, 7, false, 7),
    WAN_8(Suit.WAN, 8, false, 8),
    WAN_9(Suit.WAN, 9, false, 9),

    // 筒(Tong)
    TONG_1(Suit.TONG, 1, false, 10),
    TONG_2(Suit.TONG, 2, false, 11),
    TONG_3(Suit.TONG, 3, false, 12),
    TONG_4(Suit.TONG, 4, false, 13),
    TONG_5(Suit.TONG, 5, false, 14),
    TONG_5BAO(Suit.TONG, 5, true, 15),
    TONG_6(Suit.TONG, 6, false, 16),
    TONG_7(Suit.TONG, 7, false, 17),
    TONG_8(Suit.TONG, 8, false, 18),
    TONG_9(Suit.TONG, 9, false, 19),

    // 索(Suo)
    SUO_1(Suit.SUO, 1, false, 20),
    SUO_2(Suit.SUO, 2, false, 21),
    SUO_3(Suit.SUO, 3, false, 22),
    SUO_4(Suit.SUO, 4, false, 23),
    SUO_5(Suit.SUO, 5, false, 24),
    SUO_5BAO(Suit.SUO, 5, true, 25),
    SUO_6(Suit.SUO, 6, false, 26),
    SUO_7(Suit.SUO, 7, false, 27),
    SUO_8(Suit.SUO, 8, false, 28),
    SUO_9(Suit.SUO, 9, false, 29),

    // 字牌(Zi)
    DONG(Suit.ZI, 0, false, 30), // 东南西北中发白
    NAN(Suit.ZI, 0, false, 31),
    XI(Suit.ZI, 0, false, 32),
    BEI(Suit.ZI, 0, false, 33),
    ZHONG(Suit.ZI, 0, false, 34),
    FA(Suit.ZI, 0, false, 35),
    BAI(Suit.ZI, 0, false, 36);

    public enum Suit {
        WAN, TONG, SUO, ZI
    }

    private final Suit suit;
    private final int number;
    private final boolean isBao;
    private final int order; // 用于排序的字段

    MahjongTile(Suit suit, int number, boolean isBao, int order) {
        this.suit = suit;
        this.number = number;
        this.isBao = isBao;
        this.order = order;
    }

    public Suit getSuit() {
        return suit;
    }

    public int getNumber() {
        return number;
    }

    public boolean isBao() {
        return isBao;
    }

    public int getOrder() {
        return order;
    }
}

包括是否是宝牌,牌面数字,牌种类,是牌堆中第几张等等。
 计算器核心逻辑:

import java.util.Arrays;
import java.util.Comparator;

public class BackendCalc {
    MahjongTile[] user;
    MahBooleans boolor = new MahBooleans();
    private int finalScore;
    private void sortMahjongs(MahjongTile[] user) {
        Arrays.sort(user, Comparator.comparingInt(MahjongTile::getOrder));
    }

    public int getFinalScore() {
        return finalScore;
    }

    private boolean isTripleS(MahjongTile a, MahjongTile b, MahjongTile c){
        if(a.getSuit() != b.getSuit() || b.getSuit() != c.getSuit()){
            return false;
        }
        if(a == b &&b == c){
            return true;
        } else if (b.getNumber() == a.getNumber()+1&&c.getNumber() == b.getNumber()+1) {
            return true;
        }
        return false;
    }

    private boolean isHu(MahjongTile[] user) {
        int triple_ke_shun = 0, double_jiang_quetou = 0,flag_search = 1;
        MahjongTile temp1 = null,temp2 = null;
        for(MahjongTile i:user){
            switch (flag_search){
                case 1:
                    temp1 = i;
                    flag_search = 2;
                    break;
                case 2:
                    temp2 = i;
                    flag_search = 3;
                    break;
                case 3:
                    if(isTripleS(temp1,temp2,i)){
                        triple_ke_shun++;
                    }else if(temp1 == temp2){
                        double_jiang_quetou ++;
                    }
                    flag_search = 1;
            }
        }
        if(triple_ke_shun == 4&&double_jiang_quetou == 1){
            return true;
        }
        return false;
    }

    private int pointsCalc(MahBooleans mahBooleans) {
        // ==== 基础参数 ====
        int han = calculateHan(mahBooleans);  // 计算总番数
        int fu = calculateFu(mahBooleans);    // 计算符数
        boolean isDealer = mahBooleans.getDealer();
        boolean isTsumo = mahBooleans.getTsumo();

        // ==== 基础点数计算 ====
        int basePoints = calculateBasePoints(han, fu);

        // ==== 最终点数计算 ====
        int finalScore = calculateFinalScore(basePoints, isDealer, isTsumo);





        // 向上取整到整百
        return (int) Math.ceil(finalScore / 100.0) * 100;
    }

    // 计算总番数
    private int calculateHan(MahBooleans mb) {
        int han = 0;
        // 役种计算(示例部分)
        if(mb.getTanyao()) han += 1;       // 断幺九
        if(mb.getRiichi()) han += 1;       // 立直
        if(mb.getIppatsu()) han += 1;      // 一发
        if(mb.getMenzen() && mb.getTsumo()) han += 1; // 门清自摸
        // ...其他役种判断...
        return han;
    }

    // 符数计算
    private int calculateFu(MahBooleans mb) {
        // 特殊牌型判断
//        if(isChiitoitsu(mb)) return 25;    // 七对子
//        if(isPinfu(mb)) return mb.getTsumo() ? 20 : 30; // 平和
//
        int fu = 20; // 基础符
//
//        // 雀头加符
//        if(isYakuPair(mb)) fu += 2; // 役牌雀头
//
//        // 刻杠加符
//        fu += calculateKotsuFu(mb);  // 刻子计算
//
//        // 和牌形式
//        if(isSingleWait(mb)) fu += 2; // 单骑听牌
//        if(isEdgeOrClosedWait(mb)) fu += 2; // 边张/嵌张
        if(mb.getTsumo()) fu += 2;   // 自摸
        if(mb.getMenzen() && mb.getRon()) fu += 10; // 门清荣和

        // 向上取整到十位
        return (int) Math.ceil(fu / 10.0) * 10;
    }

    // 基础点数计算
    private int calculateBasePoints(int han, int fu) {
        if(han >= 13) return 8000;   // 役满
        if(han >= 11) return 6000;   // 三倍满
        if(han >= 8) return 4000;    // 倍满
        if(han >= 6) return 3000;    // 跳满
        if(han >= 5) return 2000;    // 满贯

        // 精确计算
        int base = fu * (int) Math.pow(2, han + 2);
        return Math.min(base, 2000); // 满贯封顶
    }

    // 最终点数分配
    private int calculateFinalScore(int base, boolean isDealer, boolean isTsumo) {
        if(isDealer) {
            return isTsumo ?
                    base * 2 * 3 :   // 自摸每家付2a
                    base * 6;        // 荣和付6a
        } else {
            return isTsumo ?
                    (base * 2) + (base * 1 * 2) : // 庄家2a 闲家1a
                    base * 4;        // 荣和付4a
        }
    }

//    // ==== 辅助方法 ====
//    private boolean isChiitoitsu(MahBooleans mb) {
//        // 判断七对子逻辑
//        return mb.getIipeiko() && ...;
//    }
//
//    private boolean isPinfu(MahBooleans mb) {
//        // 判断平和逻辑
//        return !mb.getTanyao() && ...;
//    }

    public BackendCalc(MahjongTile[] user,boolean isHubool,MahBooleans boolor) {
        this.user = user;
        this.boolor = boolor;
        sortMahjongs(user);
        if(isHu(user)){
            isHubool = true;
            finalScore = pointsCalc(boolor);
        }else {
            isHubool = false;
        }

    }
}

(本人并不懂全部的规则,所以计算部分的完善只能后人补充了)
各种各样的“是与非”
 

public class MahBooleans {
    private Boolean isDealer = false;
    /** 是否为自摸和牌(与荣和互斥) */
    private Boolean isTsumo = false;
    /** 是否为荣和(他人放铳) */
    private Boolean isRon = false;
    /** 是否为门清状态(无吃、碰、明杠) */
    private Boolean isMenzen = false;

    // ---------- 特殊和牌方式 ----------
    /** 是否立直(宣言立直) */
    private Boolean isRiichi = false;
    /** 是否一发(立直后一轮内和牌) */
    private Boolean isIppatsu = false;
    /** 是否海底摸月(自摸牌山最后一张) */
    private Boolean isHaitei = false;
    /** 是否河底捞鱼(荣和最后一张舍牌) */
    private Boolean isHoutei = false;
    /** 是否岭上开花(开杠后摸牌和牌) */
    private Boolean isRinshan = false;
    /** 是否枪杠(荣和对手加杠的牌) */
    private Boolean isChankan = false;

    // ---------- 役种与规则 ----------
    /** 是否断幺九(无幺九牌) */
    private Boolean isTanyao = false;
    /** 是否一杯口(两组相同顺子,门清限定) */
    private Boolean isIipeiko = false;
    /** 是否三色同顺(三组同数字不同色顺子) */
    private Boolean isSanshoku = false;
    /** 是否一气通贯(同一色1-9顺子) */
    private Boolean isIttsu = false;
    /** 是否混全带幺九(所有面子含幺九,副露可) */
    private Boolean isChanta = false;
    /** 是否清一色(全手牌单一色) */
    private Boolean isChinitsu = false;
    /** 是否役满(如天和、国士无双等) */
    private Boolean isYakuman = false;
    /** 是否累计役满(多个役种累计达到役满) */
    private Boolean isMultipleYakuman = false;

    // ---------- 特殊场况 ----------
    /** 是否流局满贯(流局时未鸣牌且手牌全为幺九字牌) */
    private Boolean isNagashi = false;
    /** 是否为天和(庄家起手和牌) */
    private Boolean isTenhou = false;
    /** 是否为地和(闲家第一巡和牌) */
    private Boolean isChiihou = false;

    // ---------- 宝牌相关 ----------
    /** 是否包含宝牌(需额外输入数量) */
    private Boolean hasDora = false;
    /** 是否包含里宝牌(立直后生效) */
    private Boolean hasUraDora = false;
    /** 是否包含赤宝牌(红五万/筒/索) */
    private Boolean hasRedDora = false;


    public Boolean getHasDora() {
        return hasDora;
    }

    public void setHasDora(Boolean hasDora) {
        this.hasDora = hasDora;
    }

    public Boolean getHasRedDora() {
        return hasRedDora;
    }

    public void setHasRedDora(Boolean hasRedDora) {
        this.hasRedDora = hasRedDora;
    }

    public Boolean getHasUraDora() {
        return hasUraDora;
    }

    public void setHasUraDora(Boolean hasUraDora) {
        this.hasUraDora = hasUraDora;
    }

    public Boolean getChankan() {
        return isChankan;
    }

    public void setChankan(Boolean chankan) {
        isChankan = chankan;
    }

    public Boolean getChanta() {
        return isChanta;
    }

    public void setChanta(Boolean chanta) {
        isChanta = chanta;
    }

    public Boolean getChiihou() {
        return isChiihou;
    }

    public void setChiihou(Boolean chiihou) {
        isChiihou = chiihou;
    }

    public Boolean getChinitsu() {
        return isChinitsu;
    }

    public void setChinitsu(Boolean chinitsu) {
        isChinitsu = chinitsu;
    }

    public Boolean getDealer() {
        return isDealer;
    }

    public void setDealer(Boolean dealer) {
        isDealer = dealer;
    }

    public Boolean getHaitei() {
        return isHaitei;
    }

    public void setHaitei(Boolean haitei) {
        isHaitei = haitei;
    }

    public Boolean getHoutei() {
        return isHoutei;
    }

    public void setHoutei(Boolean houtei) {
        isHoutei = houtei;
    }

    public Boolean getIipeiko() {
        return isIipeiko;
    }

    public void setIipeiko(Boolean iipeiko) {
        isIipeiko = iipeiko;
    }

    public Boolean getIppatsu() {
        return isIppatsu;
    }

    public void setIppatsu(Boolean ippatsu) {
        isIppatsu = ippatsu;
    }

    public Boolean getIttsu() {
        return isIttsu;
    }

    public void setIttsu(Boolean ittsu) {
        isIttsu = ittsu;
    }

    public Boolean getMenzen() {
        return isMenzen;
    }

    public void setMenzen(Boolean menzen) {
        isMenzen = menzen;
    }

    public Boolean getMultipleYakuman() {
        return isMultipleYakuman;
    }

    public void setMultipleYakuman(Boolean multipleYakuman) {
        isMultipleYakuman = multipleYakuman;
    }

    public Boolean getNagashi() {
        return isNagashi;
    }

    public void setNagashi(Boolean nagashi) {
        isNagashi = nagashi;
    }

    public Boolean getRiichi() {
        return isRiichi;
    }

    public void setRiichi(Boolean riichi) {
        isRiichi = riichi;
    }

    public Boolean getRinshan() {
        return isRinshan;
    }

    public void setRinshan(Boolean rinshan) {
        isRinshan = rinshan;
    }

    public Boolean getRon() {
        return isRon;
    }

    public void setRon(Boolean ron) {
        isRon = ron;
    }

    public Boolean getSanshoku() {
        return isSanshoku;
    }

    public void setSanshoku(Boolean sanshoku) {
        isSanshoku = sanshoku;
    }

    public Boolean getTanyao() {
        return isTanyao;
    }

    public void setTanyao(Boolean tanyao) {
        isTanyao = tanyao;
    }

    public Boolean getTenhou() {
        return isTenhou;
    }

    public void setTenhou(Boolean tenhou) {
        isTenhou = tenhou;
    }

    public Boolean getTsumo() {
        return isTsumo;
    }

    public void setTsumo(Boolean tsumo) {
        isTsumo = tsumo;
    }

    public Boolean getYakuman() {
        return isYakuman;
    }

    public void setYakuman(Boolean yakuman) {
        isYakuman = yakuman;
    }
}

通过这些内容,我们可以实现日麻役种的简易计算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值