英语字谜求解程序(JAVA):iMessage-GamePigeon的“字谜”

博主水平非常有限,欢迎巨佬们指正、建议!

2017年6月10日更新:
源码及数据库文件:https://github.com/daggerage/fun


背景

自前段时间不小心点错,升级了iOS10之后,就开始沉迷iMessage里的GamePigeon小游戏不能自拔。
CUP PONG有毒的操作,推推乐心机的决斗,都很棒。
然而这个叫“字谜”的游戏也真是紧张刺激…
这里写图片描述
这里写图片描述
游戏界面

游戏规则大概如上,6个字母,排出若干个单词,可以只用部分字母,但至少3个。
单词越长加分越高,1分钟内分数高者获胜。

这游戏运气好的时候能到5000多分,然而如果给的字母不太好拼的话,几百分、一千多也是经常的事…
然而这游戏似乎也没法作弊,用字典查还不如直接填上去来得快,反正填错了也不会扣分。
然后就萌生了能不能写个程序来代替我求解的想法。

思路

  1. 读取这6个字符,让它们做任意组合,再验证这个组合出来的单词存不存在。
  2. 验证的方式,可以是调用第三方库,可以是查询词典开放api,可以是将词典数据存到本地再做查询。
  3. 如果是单词,输出。

思路还是比较简单的,只是有一些实现上的细节问题。

  1. python或者java的相关第三方库我没找到。感觉应该不存在的,不用数据库怎么判断是否是合法单词?(jieba分词?没做更多了解)
  2. 词典开放api限制每小时查询次数为1000,而 A66=720 ,一次游戏就满了,还要换ip,速度也不能保证,麻烦。所以还是需要存到自己的数据库里。
  3. 全排列的算法..自己想的话还是有些头大,还是上网找找资料咯(太菜)。

实现及源码

经过将近一天的摸索踩坑,总算实现了:java+本地mysql

  1. 在网上找单词数据库,用MysqlWorkbench之类的GUI工具导入到本地数据库中(sql语句或txt、csv都行):在csdn上找到一个有大约1.5w单词的数据库(sql文件),由一个个的 INSERT 组成(找的这个版本很坑,竟然还有语法错误,需要手动改一下)。在数据库中建表,导入SQL。
  2. 输入长度为6的字符串,求出这6个单词的全排列,并利用HashSet求出全组合
  3. 查询数据库的单词与该HashSet的交集
  4. 输出

表结构
表结构,其实只用到ID和Word。(吐槽一下变量名竟然是拼音缩写,不过lx是啥?)

/* 
* WordPuzzleSix.java 
*/

import java.sql.*;
import java.util.*;

public class WordPuzzleSix {
    private final int LENGTH =6;
    private final int MIN_LENGTH=3;//单词最小长度
    private Connection conn;//存储数据库连接
    private HashSet<String> wordComb;//存储所有单词的组合
    private ArrayList<String> resultNames;//存储所有结果单词String
    private String chs;//存储输入字符串

    //初始化
    WordPuzzleSix(String chs){
        if(chs.length()==LENGTH){
            conn=getConn();
            this.chs=chs;
            wordComb=new HashSet<String>();
        }
        else{
            System.out.println("number of characters less than 3!");
        }
    }
    //获取数据库连接
    private  Connection getConn() {
        String driver = "com.mysql.jdbc.Driver";//MySQL数据库驱动
        String url = "jdbc:mysql://localhost:3306/fun?characterEncoding=utf-8&useSSL=false";//数据库路径,fun为数据库名
        String username = "root";
        String password = "";
        try {
            Class.forName(driver);//加载数据库驱动
            conn = DriverManager.getConnection(url, username, password);//获取数据库连接
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    //关闭数据库连接
    private void closeConn(){
        try {
            conn.close();
        }catch (SQLException e){
            e.printStackTrace();
        }
    }

    private void swap(char[] s,int a,int b){
        char temp;
        temp=s[a];
        s[a]=s[b];
        s[b]=temp;
    }
    //递归求全排列
    private void permuatation(char[] s,int start,int len) {
        if (start > len-1) {
            String word = "";
            for (int i = 0; i < len; i++) {
                word += s[i];
            }
            for (int i = MIN_LENGTH; i <= LENGTH; i++) {
                wordComb.add(word.substring(0,i));//利用HashSet求全组合
            }
        }
        else{
            for (int i = start; i < len; i++) {
                swap(s,start,i);
                permuatation(s,start+1,len);
                swap(s,start,i);
            }
        }
    }
    //构建SQL语句
    private String formatSql(HashSet<String> hs){
        String sql = "SELECT * FROM words WHERE word IN (";
        int i=0;
        for(String name:hs){
            if(i==0){
                sql+=String.format("'%s'",name);
            }
            else{
                sql+=String.format(",'%s'",name);
            }
            i++;
        }
        sql+=")";
        return sql;
    }

    private void printResult(){
        System.out.println("#####"+chs+"#####");
        int i=0;
        for (String name:resultNames){
            i++;
            System.out.println(i+" "+name);
        }
    }

    //主方法
    public void findAllWords(){
        //将读入的的String转成char[]
        char[] c=new char[LENGTH];
        for (int i = 0; i < LENGTH; i++) {
            c[i]=chs.charAt(i);
        }
        permuatation(c,0, LENGTH);//进行递归全排列

        resultNames=new ArrayList<String>();//记录最终结果
        String sql=formatSql(wordComb);

        try {
            Statement st=conn.createStatement();
            ResultSet rs=st.executeQuery(sql);//执行组合成的sql语句

            //取出查询到的结果集,只取word字段的值
            while (rs.next()){
                String name=rs.getString("word");
                resultNames.add(name);//添加到结果集中
            }
        }catch (SQLException e){
            e.printStackTrace();
        }

        //对结果集按字符串长度,升序排列
        resultNames.sort(new Comparator<String>() {
            @Override //重写compare方法
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });
        printResult();
        closeConn();//别忘了关闭连接
    }

    //一开始使用的方法,已废弃
    private Word select(String word){
        PreparedStatement pstm;
        try {
            String sql = "SELECT * FROM words WHERE word=?";
            pstm=conn.prepareStatement(sql);
            pstm.setString(1,word);
            ResultSet rs = pstm.executeQuery();
            if(rs.next()){
                String wordName=rs.getString("word");
                Word w=new Word(wordName);
                return w;
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
        return null;
    }

}
//Select方法用到的实体(Entity)类,已废弃
class Word{
    public String word;
    Word(String word){
        this.word=word;
    }
}
/*
* Main.java
*/
import java.util.Scanner;

public class Main {
    //主程序
    public static void main(String[] args) {
        Scanner s=new Scanner(System.in);
        while (true){
            String input=s.nextLine();//读取输入字符串
            if(!input.equals("quit")) {
                if(input.length()==6){
                    long startTime=System.currentTimeMillis();

                    WordPuzzleSix wps = new WordPuzzleSix(input);
                    wps.findAllWords();

                    long endTime=System.currentTimeMillis();
                    long timeCost=endTime-startTime; //计算消耗时间
                    System.out.println("time cost : "+timeCost+" ms");
                }
                else{
                    System.out.println("number of characters less than 3!");
                }
            }
            else{
                break;
            }
        }
        s.close();
    }
}

最后结果:
运行结果

总结

虽然程序很小很简单,但写一遍下来,也对之前不是很清楚的地方熟悉了很多吧。

用到的知识点:

  1. 数据库操作,定义(DML,DDL
  2. JDBC连接、操作MySQL数据库,以及流程优化
  3. 全排列算法的递归实现与改动
  4. ArrayListHashSet 以及ArrayList中sort 方法的使用(匿名内部类,方法重写)
  5. try...catch 异常处理
  6. Java I/O

实现方向的选择:

  1. 为什么用Java:感觉用Python会方便一些,但因为Windows10下python-mysql的包难以配置(很久之前试过,放弃了),总是有各种版本问题,而java的jdbc已经有储备,就用了Java。python还是在linux下方便,而我总不会因为玩个字谜游戏切去linux吧
  2. 关于怎么查单词:可以看到,select方法和Word实体类被抛弃了。其实一开始是用循环select的方法进行查询,即一共要进行900次左右的select,在只进行一次数据库连接(getConnection()),不断开的情况下,做完这些查询耗时大约在17~60秒,速度太慢了,而且很不稳定,不知道为什么。所以最后采用了WHERE ... IN ()的方式进行集合查询,耗时单位都改成毫秒了。得出结论,在数据库端做一条很复杂、数据很多的查询,也比反复做多条查询速度要快。由于对数据库的了解不深,背后具体的原理日后可能更新。

不足:

  1. 因为数据库比较小(跟游戏的相比),每次查出来的结果都不是很多,都输进去也就4000-6000分的样子,而且这游戏很鬼畜,一个名词,加s变复数后算一个新单词,而这个程序并没有选出复数、过去式等等(也不一定每局游戏都多个s),所以还是得手动+1s,以及考察对单词过去式的记忆~
  2. 项目结构可能不是特别好,稍显凌乱?不过也就一个小功能,也差不多?如果有大佬能指点一番,不胜感激!
  3. 自己太菜了,感觉还是写的比较慢

后记

最后实际效果还是蛮好的,胜率大幅提升23333

当然也不是无敌的,有时候还是感叹AI无法战胜人类啊~附一张对战截图(“我竟然输给了人类?!”)
这里写图片描述

其实是故意让的,总是赢的话,女票都不跟自己玩了(逃

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值