关于24点

博主简单研究24点游戏知识后,编写程序解24点,并制作了24点“查询辞典”。为让“辞典”更简洁,采用哈希去重策略,虽有瑕疵但效果不错,后续计划深入研究。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近简单地研究了一些关于二十四点游戏的知识,然后写了一个挺zz的程序去解24点,后来又用他做了一个24点“查询辞典”之类的东西,感觉还不错。以后有时间的话再进行深入研究,先把程序挂在这儿。

/// Day37 2019.5.1 二十四点字典 

#include <cstdio>
#include <set>     /// fot set
#include <cmath>   /// for pow, fabs
#include <cstring> /// for memset, strcpy
#include <cctype>
#include <algorithm>
#include <ctime>  /// for clock
using namespace std;

double replace(double x, int t) { /// 替代值 (t!=0) 
    return pow(x, 1.0/t);
    /// t = 1 表示不改变原有的值 
    /// else 表示用一个无理数替代原有的值 
}

struct stringFile { /// 用于从字符串中截取 
    char str[128]; int top;
    void load(const char* ch) { /// 装载字符串 
        memset(str, 0x00, sizeof(str));
        top = 0;
        for(int i = 0; ; i ++) {
            str[i] = ch[i];
            if(ch[i] == 0) return;
        }
    }
    char front() { /// 获得第一个字符 
        return str[top];
    }
    void pop() { /// 弹出第一个字符 
        if(str[top] != 0) top ++;
    }
    bool end() { /// 判断是否已经到文件结尾 
        return str[top] == 0;
    }
    void jump() { /// 跳过首端空格 
        while(!end() && !isgraph(front())) {
            pop();
        }
    }
    void readstr(char* sto) { /// 读取一个相连串 
        jump(); /// 跳过首端空格
        if(end()) {
            sto[0] = 0; /// 什么都不剩了 
        }else {
            sto[0] = front(); pop();
            if(sto[0]=='(' || sto[0]==')') {
                sto[1] = 0; /// 读到括号 读取完成 
                return;
            }else if(sto[0]=='+' || sto[0]=='-' ||
                     sto[0]=='*' || sto[0]=='/') {
                sto[1] = 0; /// 读到运算符 读取完成 
                return;
            }else if('0'<=sto[0] && sto[0]<='9') { /// 发现数值 
                for(int i = 1; ; i ++) {
                    if('0'<=front() && front()<='9') {
                        sto[i] = front(); pop(); /// 都区连续数值 
                    }else {
                        sto[i] = 0; break;
                    }
                }
                return;
            }else {
                strcpy(sto, "Error");
                /// 发现未知字符 
            }
        }
    }
};

const double erroNum = -123456789.0;

char opes[5] = {0, '+', '-', '*', '/'};
double cal(double x, char ope, double y) {
    switch(ope) {
        case '+':
            if(fabs(x-erroNum)<1e-6 || fabs(y-erroNum)<1e-6) return erroNum; /// 计算错误的传递 
            return x+y;
        case '-':
            if(fabs(x-erroNum)<1e-6 || fabs(y-erroNum)<1e-6) return erroNum;
            return x-y;
        case '*':
            if(fabs(x-erroNum)<1e-6 || fabs(y-erroNum)<1e-6) return erroNum;
            return x*y;
        case '/': 
            if(fabs(y) < 1e-6) { /// 除以零 
                /// 忽略这个计算错误即可 
                return erroNum; /// error
            }else {
                return x/y;
            }
        default:
            /// 这个错误 不能忽略
            fprintf(stderr, "调用 cal 函数式 算符 ope{%c} 非法", ope);
            while(1);
            return erroNum; /// error
    }
}

double get(stringFile& sf, int t) { /// 得到计算结果 
    char tmp[64];
    sf.readstr(tmp); /// 读取一个字符串 
    if('0'<=tmp[0] && tmp[0]<='9') { /// 遇到数值 
        return replace(atof(tmp), t);
        /// 对于真实的数值采用替代符
        /// 对于中间结果不采用替代符 
    }else {
        if(tmp[0] == '(') { /// 表达式的开始 
            double x, y, ans;
            char ope[16];
            x = get(sf, t);  /// 得到第一个参数 
            sf.readstr(ope); /// 得到算符 
            y = get(sf, t);  /// 得到第二个参数 
            ans = cal(x, ope[0], y); /// 得到计算结果 
            sf.readstr(tmp); /// 去掉剩下的一个右括号 
            return ans;
            if(tmp[0] != ')') {
                fprintf(stderr, "表达式缺少右括号!");
                while(1);
            }
        }else {
            ///发现错误 
            fprintf(stderr, "表达式中出现不合法的开头标签{%s}", tmp);
            while(1); 
        }
    }
}

/// 定义表达式的格式: 
/// 1.一个数 
/// 2.左括号 + 表达式 + 运算符 + 表达式 + 右括号 
/// 注意: 对于所有运算都强制使用括号  

struct Exp {      /// 描述一个表达式  
    char str[64]; /// 以字符串的形式储存表达式 
    void load(const char* ch) {
        strcpy(str, ch);
    }
    double getval(int t = 1) { /// 得到计算结果 
        stringFile sf;
        sf.load(str); /// 加载字符串 
        return get(sf, t);
    }
};

struct feature { /// 描述一个表达式的特征 
    long long a[6]; /// a[1] to a[5]
    void load(Exp& exp) { /// 得到一个表达式的特征 
        for(int i = 1; i <= 5; i ++) {
            a[i] = exp.getval(i) * 1000; /// 考虑小数点后三位 
        }
    }
};

bool operator < (const feature& f1, const feature& f2) {
    for(int i = 1; i <= 5; i ++) {
        if(f1.a[i] != f2.a[i]) /// 发现不同 
            return f1.a[i] < f2.a[i];
    }
    return false; /// 相同 
}

bool operator == (const feature& f1, const feature& f2) {
    /// 判断两个特征数列是否相同 
    for(int i = 1; i <= 5; i ++) {
        if(f1.a[i] != f2.a[i]) /// 发现不同 
            return false;
    }
    return true; /// 相同 
}

set<feature> dic;

int ary[25][5] = { /// 枚举全排列 
    {0, 0, 0, 0, 0},
    
    {0, 1, 2, 3, 4},
    {0, 1, 2, 4, 3},
    {0, 1, 3, 2, 4},
    {0, 1, 3, 4, 2},
    {0, 1, 4, 2, 3},
    {0, 1, 4, 2, 3},
    
    {0, 2, 1, 3, 4},
    {0, 2, 1, 4, 3},
    {0, 2, 3, 1, 4},
    {0, 2, 3, 4, 1},
    {0, 2, 4, 1, 3},
    {0, 2, 4, 3, 1},
    
    {0, 3, 1, 2, 4},
    {0, 3, 1, 4, 2},
    {0, 3, 2, 1, 4},
    {0, 3, 2, 4, 1},
    {0, 3, 4, 1, 2},
    {0, 3, 4, 2, 1},
    
    {0, 4, 1, 2, 3},
    {0, 4, 1, 3, 2},
    {0, 4, 2, 1, 3},
    {0, 4, 2, 3, 1},
    {0, 4, 3, 1, 2},
    {0, 4, 3, 2, 1}
};

int cnt;
void solve(double A, double B, double C, double D) {
    dic.clear();
    cnt = 0;
    double carrier[5] = {0, A, B, C, D};
    for(int d = 1; d <= 24; d ++) { /// 枚举全排列  
        A = carrier[ary[d][1]];
        B = carrier[ary[d][2]];
        C = carrier[ary[d][3]];
        D = carrier[ary[d][4]]; /// 枚举全排列 
        for(int i = 1; i <= 4; i ++) {
            for(int j = 1; j <= 4; j ++) {
                for(int k = 1; k <= 4; k ++) {
                    /// 枚举三个运算符 
                    char tmp[64]; Exp exp; feature ftr;
                    
                    /// Mode 1
                    sprintf(tmp, "((%d %c %d) %c (%d %c %d))",
                        int(A+0.1), opes[i], int(B+0.1), opes[j], int(C+0.1), opes[k], int(D+0.1));
                    exp.load(tmp);
                    ftr.load(exp); /// 加载表达式 
                    if(ftr.a[1]==24000 && dic.find(ftr) == dic.end()) {
                        dic.insert(ftr);
                        //printf("  %s\n", tmp);
                        cnt ++;
                    }
                    
                    /// Mode 2
                    sprintf(tmp, "(((%d %c %d) %c %d) %c %d)",
                        int(A+0.1), opes[i], int(B+0.1), opes[j], int(C+0.1), opes[k], int(D+0.1));
                    exp.load(tmp);
                    ftr.load(exp); /// 加载表达式 
                    if(ftr.a[1]==24000 && dic.find(ftr) == dic.end()) {
                        dic.insert(ftr);
                        //printf("  %s\n", tmp);
                        cnt ++;
                    }
                    
                    /// Mode 3
                    sprintf(tmp, "((%d %c (%d %c %d)) %c %d)",
                        int(A+0.1), opes[i], int(B+0.1), opes[j], int(C+0.1), opes[k], int(D+0.1));
                    exp.load(tmp);
                    ftr.load(exp); /// 加载表达式 
                    if(ftr.a[1]==24000 && dic.find(ftr) == dic.end()) {
                        dic.insert(ftr);
                        //printf("  %s\n", tmp);
                        cnt ++;
                    }
                    
                    /// Mode 4
                    sprintf(tmp, "(%d %c ((%d %c %d) %c %d))",
                        int(A+0.1), opes[i], int(B+0.1), opes[j], int(C+0.1), opes[k], int(D+0.1));
                    exp.load(tmp);
                    ftr.load(exp); /// 加载表达式 
                    if(ftr.a[1]==24000 && dic.find(ftr) == dic.end()) {
                        dic.insert(ftr);
                        //printf("  %s\n", tmp);
                        cnt ++;
                    }
                    
                    /// Mode 5
                    sprintf(tmp, "(%d %c (%d %c (%d %c %d)))",
                        int(A+0.1), opes[i], int(B+0.1), opes[j], int(C+0.1), opes[k], int(D+0.1));
                    exp.load(tmp);
                    ftr.load(exp); /// 加载表达式 
                    if(ftr.a[1]==24000 && dic.find(ftr) == dic.end()) {
                        dic.insert(ftr);
                        //printf("  %s\n", tmp);
                        cnt ++;
                    }
                }
            }
        }
    }
    //if(cnt == 0) {
    //    printf("  No solution.\n");
    //}
}

int main() {
    freopen("a.txt", "w", stdout);
    int p = 0, q = 0;
    for(int A = 1; A <= 13; A ++) {
        for(int B = A; B <= 13; B ++) {
            for(int C = B; C <= 13; C ++) {
                for(int D = C; D <= 13; D ++) {
                    if(A<=10 && B<=10 && C<=10 && D<=10) continue; /// 至少 JQK 中的一个  
                    fprintf(stderr, "A = %2d, B = %2d, C = %2d, D = %2d, clock = %d\n",
                        A, B, C, D, clock());
                    solve(A, B, C, D);
                    if(cnt == 0) {
                        if(p != A) { /// 新一段的开始 
                            printf("----------\n");
                            printf("--> %d %d %d %d\n", A, B, C, D);
                            p = A; q = B;
                        }else if(q != B) { /// 新一子段的开始 
                            printf("--> %d %d %d %d\n", A, B, C, D);
                            q = B;
                        }else {
                            printf("-- %d %d %d %d\n", A, B, C, D);
                        }
                    }
                }
            }
        }
    }
    freopen("CON", "w", stdout);
    return 0;
}

为了让“辞典”更加简洁,上面采用了一种哈希去重的策略,虽说有一些瑕疵,但是效果很棒(比如会把X+(Y-Y)X*(Y/Y)视为等价表达式)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值