看图猜成语通关辅助程序(ruby)

      过年前的时候,看到同事HQJ在手机上玩猜成语,前两天,我也下载了一个《疯狂看图猜成语2》,其实最开始看到这个游戏时候,就习惯性的想作弊了。
      玩游戏我一直喜欢修改的,比如FPE啦,金山游侠啦,对我来说都是玩游戏必备的工具。
      这个看图猜成语,就是给一幅图,下面是24个汉字,从里面选4个字,作为符合图内容的成语答案。
      ok,现在让我们go。

系统:Windows XP sp2
ruby:1.9.2p180
编辑器:SciTE 1.73

      工作环境,无法上网,所以系统旧,ruby旧,大家先承担些,后面没问题了,再转Ubuntu(Ubuntu在u盘上安装的系统,比windows下还慢)。

      1.开始的时候,很简单的想法,就是从里面随便抽汉字,组成4个字的词,看里面是否有成语,这个是最简单的。
      我喜欢使用ruby,纯属个人爱好,代码也许很烂,看的人多担待些。
      在Windows下有个问题,操作系统的编码是gbk,但是ruby1.9底层是utf-8,所以总是需要转码。

#coding: UTF-8
require 'iconv'  
words_array = %w|无 黑 用 如 小 比 伦 材 脚 纸 四 表 两 得 一 字 天 大 朝 白 与 举 一 里|   #成语游戏的备选字库
words = words_array.map {|x|  Iconv.conv 'gbk', 'utf-8', x} #utf-8转gbk
idioms = words.permutation(4).to_a #每4个字分组
idioms.each {|x|  puts x.join}  #分组合并,就是四字成语

      功能是有了,但是这个排列组合的数量巨大,人眼很难找到成语,24个字4字排列总共255024个,眼神好能找到符合的成语,不好就很难了。

      2.数量巨大的结果不实用,那么就减少。一个想法是从网上弄成语词库,从里面选就可以了。
      去网上找成语词库,真的不怎么好找,都是带解释什么的乱七八糟的,最后找到一个叫“成语词典.txt”的文件,里面的内容是类似“【束教管闻】谓学识浅陋,见闻不广。”这样的内容内容,就他了,很容易通过正则取出【】中间的内容为成语词典。

#coding: UTF-8
require 'iconv'
s1 = Iconv.conv 'gbk','utf-8',"" #烦人的转码
s2 = Iconv.conv 'gbk','utf-8',""
File.open('成语.txt','w') do |wfile|
  File.open('成语词典.txt','r') do |rfile|
    rfile.each do |line|
      line =~ /#{s1}(.*)#{s2}/
      wfile << $1 << "\n"  #没有\n就会连一起了
    end
  end
end

      输出为“成语.txt”,最后有几个错误的词,手工给删除了,最后是23594条成语。

      3.有字库排列,有成语词典,第一个想法当然是排列对照,简单的就是两个循环。其实我最开始就是这么做的,比较丢人啊,就不写了,速度很慢,你想想255024*23594的循环。后来就问了HQJ,程序员就明白的多,告诉我既然成语词典少,就从成语词典过滤,先比较第一个字,这样一次就只有几十上百个词语选择,接着再去字库中选择。

      按照这个思路,写了几版,最终的定型大概是这样的。

#coding: UTF-8
require 'iconv'  
words = %w|无 黑 用 如 小 比 伦 材 脚 纸 四 表 两 得 一 字 天 大 朝 白 与 举 一 里|

idioms_array = Array.new
idioms_regexp  = Array.new

File.open('成语.txt','r') do |file|
  file.each do |line|
    idioms_array << line.chomp   #把成语词典折腾出来,作为array数组     
  end
end

words_g = words.map {|x| Iconv.conv 'gbk', 'utf-8',x}.uniq #转码,另外把重复的字去除,后面用不到

words_g.each {|word|
  idioms_array.map {|x| idioms_regexp << x if x =~ /^#{word}/} #循环,从成语词典中匹配第一个字相同的记录
  idioms_regexp.map  {|idiom|
    idioms_regexp = []   #这里坑了我很久,一直有重复的,后来发现因为里面有数据,清空就好了
    puts idiom if words_g.include?(idiom[1]) && words_g.include?(idiom[2]) && words_g.include?(idiom[3]) #检查成语的第2、3、4个字,是否在词库中,都在那就ok了
  }
}


      4.上面的想法都太直白了,还是按照ruby的习惯,尽可能的少的代码,把循环等去除一下。

      重构code:

#coding: UTF-8
require 'iconv'
require 'benchmark'

words = %w|无 黑 用 如 小 比 伦 材 脚 纸 四 表 两 得 一 字 天 大 朝 白 与 举 一 里|

idioms_array = Array.new

File.open('成语.txt','r') do |file|
  file.each do |line|
    idioms_array << line.chomp   #把成语词典折腾出来,作为array数组     
  end
end

words_g = words.map {|x| Iconv.conv 'gbk', 'utf-8',x}.uniq #转码,另外把重复的字去除,后面用不到

Benchmark.bm do |bm|
    bm.report {
    words_g.each do |word|
      idioms_array.map do |idiom|
        puts idiom if idiom =~ /^#{word}/ and words_g.include?(idiom[1]) and words_g.include?(idiom[2]) and words_g.include?(idiom[3])   
      end
    end
  }
end


      我的烂机器上,大概6.8秒。重构与否其实速度没有变化。本来还序列化的了,但是和直接读txt一样,所以就没有用。

      5.Ubuntu下使用下面的代码。因为我的Ubuntu性能更烂,所以需要近10秒。而且Ubuntu下,需要把成语.txt转换为utf-8格式,所以我用了“成语u.txt”。

Ubuntu下代码:

#coding: UTF-8
require 'iconv'
require 'benchmark'

words = %w|无 黑 用 如 小 比 伦 材 脚 纸 四 表 两 得 一 字 天 大 朝 白 与 举 一 里|
idioms_array = Array.new

File.open('成语u.txt','r') do |file|
  file.each do |line|
    idioms_array << line.chomp   #把成语词典折腾出来,作为array数组
  end
end

Benchmark.bm do |bm|
  bm.report {
    words.uniq.each do |word|
      idioms_array.map do |idiom|
        puts idiom if idiom =~ /^#{word}/ and words.include?(idiom[1]) and words.include?(idiom[2]) and words.include?(idiom[3])
      end
    end
  }
end

     

      6.看到这里,大家发现了什么没有,其实上面说的都是在扯淡,不过恐怕没有几个人有耐心看到这里的吧。我的处理思想是有问题的,其实完全想多了。最简单的就是,从成语文件中读取,判断是否在字库中存在即可,下面的代码是秒速的,不到0.2秒结果就出来了,快2个数量级的差别。

Windows版本:

#coding: UTF-8
require 'iconv'
require 'benchmark'

words = %w|无 黑 用 如 小 比 伦 材 脚 纸 四 表 两 得 一 字 天 大 朝 白 与 举 一 里|

words_g = words.map {|x| Iconv.conv 'gbk', 'utf-8',x}.uniq #转码,另外把重复的字去除,后面用不到

Benchmark.bm do |bm|
  bm.report {
    File.open('成语.txt','r') do |file|
      file.each do |line|
        puts line if words_g.include?(line[0]) and words_g.include?(line[1]) and words_g.include?(line[2]) and words_g.include?(line[3])
      end
    end
  }
end


Ubuntu版本:

#coding: UTF-8
require 'benchmark'

words = %w|无 黑 用 如 小 比 伦 材 脚 纸 四 表 两 得 一 字 天 大 朝 白 与 举 一 里|

Benchmark.bm do |bm|
  bm.report {
    File.open('成语u.txt','r') do |file|
      file.each do |line|
        puts line if words.include?(line[0]) and words.include?(line[1]) and words.include?(line[2]) and words.include?(line[3])
      end
    end
  }
end


      7.大家使用的时候,可以把benchmark的部分屏蔽,反正就是要个结果而已。还有,因为我用的成语词典库不大,所以有很多查不到的,比如286关“胜友如云”,还有“置之脑后”"不可造次""巧用天时""寸土如金"“自始至终”“串通一气”等,遇上这些,大家还是用金币吧,不过应该不是很多。

      代码我放到下面的链接中了,只放最终的代码
      感谢能看到这里的朋友。

 

posted @ 2014-02-26 18:06  pyp(鹿鸣)  阅读(1287)  评论(0编辑  收藏  举报