R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。
R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。
要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。
关于作者:
- 张丹(Conan), 程序员Java,R,PHP,Javascript
- weibo:@Conan_Z
- blog: http://blog.fens.me
- email: bsspirit@gmail.com
转载请注明出处:
http://blog.fens.me/r-game-gridgame/
前言
为了能发布自己的游戏包,我们已经储备了很多的基础知识,包括 R语言面向对象编程、R包开发编程、R语言游戏编程 等,最后一步就到综合运用的时候了。按CRAN的R包发布的要求,把所有代码和文档串起来,就是我们要发布的gridgame游戏包。
目录
- 知识储备
- gridgame包开发
- gridgame包发布
1. 知识储备
在综合运用所有知识前,先回顾一下,我们都需要掌握哪些知识,1)R语言编程的基础是必需,2)游戏的算法主要是对矩阵的操作,线性代数最好也要掌握,3)游戏操作需要有界面,虽然不需要太好看,但我们也要有能力用代码画出游戏界面来,4)游戏的框架封装,对于相同类型的游戏,如果第二款游戏能延续第一款游戏的结构,不仅能简化开发节约时间,还能降低游戏技术门槛,就需要用面向对象的思维模式对游戏框架进行封装。
1.1 基础知识
上面列出的4个主要知识点,我都做技术准备,针对不同问题,参考不同文章就可以解决:
- R语言面向对象编程:R语言基于RC的面向对象编程
- R语言环境空间:揭开R语言中环境空间的神秘面纱, 解密R语言函数的环境空间
- R语言游戏编程:R语言键盘和鼠标事件, R语言游戏之旅 贪食蛇入门, R语言游戏框架设计, R语言游戏之旅 游戏2048
- R包开发编程:开发自己的R包sayHello, 在巨人的肩膀前行 催化R包开发
1.2 给R包起名
技术准备一切就绪,先给项目起个名字吧!
其实,起名也是有讲究的,不能太随便,虽然CRAN上面没有叫game包,但我们直接用game做为项目名字,也不是很好。如何起名最好先问问Google,找一个没有太多重名的关键字作为名字。
直接用google搜索 game 有 1,100,000,000 条结果,r game 有1,680,000,000条结果。我连0都数不过来了!和这么多搜索结果去竞争关键字,是我们在推广过程中不可逾越的鸿沟。
所以,换个名字让我们从小成长,找一个热度不是那么高的关键字,作为项目名字。gridgame就是一个不错的选择,既能表现游戏的特征,从推广角度又没有特别强大的竞争对手,一下子压力全无,倍感轻松!
做好了各种准备工作,下面就是把gridgame包搞定了!
2. gridgame包开发
按照 在巨人的肩膀前行 催化R包开发 文章中介绍的标准化的开发流程,进行R包的开发。
本文的系统环境
- Win7 64bit
- R version 3.0.3 x86_64-w64-mingw32/x64 (64-bit)
- Rtools31.exe
- basic-miktex-2.9.5105.exe
由于有gridgame包开发的游戏都是基于Window平台的,而且我不打算支持Linux平台运行,所以我们的选择的R包开发环境,也应该是Window平台。后文中会介绍,为什么不支持Linux平台运行,R的版本最好也升级到3.0.3。(坑很多的,要一点一点填!)
2.1 构建工程
为了简化工作量,提高开发效率,我将按照devtools包的开发流程进行操作。
首先,打开一个项目目录,然后创建项目骨架。
~ cd /home/conan/R
~ R
# 加载开发工具包
> library(devtools)
> library(roxygen2)
Loading required package: digest
> library(testthat)
# 创建项目骨架
> create(paste(getwd(),"/gridgame",sep=""))
Creating package gridgame in /home/conan/R
No DESCRIPTION found. Creating default:
Package: gridgame
Title:
Description:
Version: 0.1
Authors@R: # getOptions('devtools.desc.author')
Depends: R (>= 3.0.1)
License: # getOptions('devtools.desc.license')
LazyData: true
# 重置当前目录
> setwd(paste(getwd(),"/gridgame",sep=""))
# 查看项目骨架生成的文件
> dir(full.names=TRUE)
[1] "./DESCRIPTION" "./man" "./R"
2.2 编写代码和文档
1). 编辑DESCRIPTION文件,增加项目的描述信息。
~ vi /home/conan/R/gridgame/DESCRIPTION
Package: gridgame
Type: Package
Title: A game framework for R
Version: 0.0.1
Date: 2014-07-23
Authors@R: c(person("Dan", "Zhang", email = "bsspirit@gmail.com", role
=c("aut", "cre")))
Maintainer: Dan Zhang
Description: This package provides a general-purpose game framework for grid game in R. The package includes 2 games about snake and 2048. You can run the function snake() or g2048() to startup the game. These games are only running on Window platform.
Depends:
R (>= 3.0.1)
Imports:
methods
License: GPL-3
URL: http://onbook.me/#/project/gridgame
BugReports: https://github.com/bsspirit/gridgame/issues
OS_type: windows
Collate:
'game.R'
'2048.R'
'package.R'
'snake.R'
DESCRIPTION文件中,有两个地方需要注意:
- Imports: methods,由于我们用的是RC类型,系统默认会用methods包中的函数进行解析,必需在这里显示声明。
- OS_type: windows,由于我们只仅支持Window系统运行,因此在后面上传到CRAN检查时,必需要指明支持的系统。同时,也是由于加了这个属性,再到Linux中执行R CMD check 过程的时候会自动失败,因此要求我们只能在windows系统中进行打包和发布的操作了。
2). 复制我们已经完成的3个R文件到 /home/conan/R/gridgame/R 目录下面。
~ ls -l
-rw-rw-r-- 1 conan conan 5030 7月 23 17:23 2048.R
-rw-rw-r-- 1 conan conan 2151 7月 23 17:18 game.R
-rw-rw-r-- 1 conan conan 5204 7月 23 17:23 snake.R
我们需要对代码进行再整理,都是检查失败错误。
- 1. 去掉2048.R和snake.R代码中source()函数对game.R文件的引用。
- 2. 去掉代码中所有中文注释,只能是ASCII码支持的字符。
- 3. 在代码中增加Window平台检查,非Window则禁止运行,通过 .Platform$OS.type==’windows’ 代码,判断运行时的系统环境。
- 4. 增加package.R文件,用于加载methods包的配置信息,#’ @import methods。
- 5. 去掉代码中启动函数,启动交给用户来操作。
2.3 调试程序
在Windows系统开发环境中,通过load_all()函数加载程序包,然后运行一个snake()函数 或者 g2048()函数,一切正常。
# 加载程序包
> load_all(getwd())
Loading gridgame
# 启动snake游戏
> snake()
# 启动2048游戏
> g2048()
我们写的游戏程序,都是在window7下开发的,运行一切正常,那么为什么不支持在Linux系统中运行呢? 主要原因是 Linux和Window7有不同的图形设备输出。windows系统中,输出设备是通过.net框架来支持的;而Linux系统中的输出设备是X11()显示驱动支持,或者通过在Linux系统中加载第三方的tk设备支持,R语言通过tkrplot包来实现调用。
~ sudo apt-get install tk-dev
> install.packages("tkrplot")
Windows系统和Linux系统对于运行GUI程序是有区别的,无法统一用一套代码来完成。有人会说,每个地方都增加系统类型的判断条件,就能可实现了。
当然情况并不是这么简单,除了输出设备的问题以外,Linux上面还会遇到字体的问题,字库不全将导致字符加载失败的错误。下面的问题,Linux系统中没有helvetica的60号字体。
Error in text.default(0.5, 0.7, label = name, cex = 5) :
X11 font -adobe-helvetica-%s-%s-*-*-%d-*-*-*-*-*-*-*, face 1 at size 60 could not be loaded
虽然在Linux Ubuntu环境中,我尝试了安装所有字体库,仍然无法解决大号字体的加载问题。所以,我决定最终将不支持Linux平台。
~ sudo apt-get install xfont-100dpi xfont-75dpi xfont-cyrillic xfont-*
2.4 单元测试
在inst/test目录下面,我们分别针对不同的文件,创建单元测试类。
- test-game.R, 对game.R的函数进行单元测试
- test-snake.R,对snake.R的函数进行单元测试
- test-2048.R,对2048.R的函数进行单元测试
以test-game.R为例,打开test-game.R文件
~ vi inst/test/test-game.R
context("game")
test_that("Initial the construct function of Game class", {
name<-"R"
width<-height<-10
game<-Game$new()
game$initFields(name=name,width=width,height=height)
expect_that(game$name,equals(name))
expect_that(game$width,equals(width))
expect_that(game$height,equals(height))
})
执行单元测试代码。
> test(getwd())
Testing gridgame
Loading gridgame
game : ...
2.5 撰写文档
R包开发中最繁琐的一个过程,就是撰写文档,Latex的格式文档。幸好有人发明了roxygen2包,通过简单的注释规则来生成Latex文档。
我们用roxygen2包,来生成man/*.Rd的文档文件,对RC类型的程序,其实就可以偷点懒了,只是在类的定义上,增加注释就行了。RC类中方法的注释,就没强制的检查了。多写少写就看开发者的心情了。如果是S3的编程方式或者纯函数式的包,那么写文档也是一件很辛苦的工程呢。当然,文件不能出现中文字符,不然check过程的时候,还是会有警告发生的。
以snake.R文件中注释为例,我们只写setRefClass的注释 和 snake<-function(){}的注释就行了,Snake类的内部方法就省略了。
~ vi snake.R
#' Snake game class
#' @include game.R
Snake<-setRefClass("Snake",contains="Game",
...
)
#' Snake game function
#'
#' @export
snake<-function(){
game<-Snake$new()
game$initFields()
game$run()
}
通过代码中的注释生成Latex文件。
> document(getwd())
Updating gridgame documentation
Loading gridgame
Writing G2048-class.Rd
Writing g2048.Rd
Writing Game-class.Rd
Writing Snake-class.Rd
Writing snake.Rd
打开snake.Rd文件,看看生成的内容。
~ vi man/snake.Rd
% Generated by roxygen2 (4.0.1): do not edit by hand
\name{snake}
\alias{snake}
\title{Snake game function}
\usage{
snake()
}
\description{
Snake game function
}
这一步的操作过程,其实也不是一帆风顺的。在引用roxygen2 包的时候,我同样遇到的问题了。
在Window环境中,roxygen2包 依赖于R 3.0.2以上的版本,R 3.0.1版本的R程序装不上roxygen2包。github有对这个强依赖问题的描述:https://github.com/klutometis/roxygen/issues/163
所以,上文中指定的R 3.0.3的版本环境,是实践检验出来的。
2.6 程序检查
程序检查,这一步其实是所有操作过程最容易出错的,而且还搞不定。R的打包的检查真的很严格啊!!
在Windows平台中开发R包,要装额外装两个软件 Rtools(http://cran.us.r-project.org/bin/windows/Rtools/) 和 MikTeX (http://www.miktex.org/download),不仅版本要和R语言环境对上,还要配置环境变量。MikTeX在调用过程中,还会遇到文件找不到,pdflatex.exe运行的错误 等等。
比如,其中的一个常见错误:
* checking PDF version of manual ... WARNING
LaTeX errors when creating PDF version.
This typically indicates Rd problems.
LaTeX errors found:
!pdfTeX error: pdflatex.EXE (file ts1-zi4r): Font ts1-zi4r at 540 not found
==> Fatal error occurred, no output PDF file produced!
* checking PDF version of manual without hyperrefs or index ... ERROR
解决方法,运行下面的命令。
~ updmap
~ initexmf --update-fndb
~ initexmf --edit-config-file updmap
# 在文件中增加一行
Map zi4.map
~ initexmf --mkmaps
最后,如果经过9*9=81难,终于修成正果,一路OK的完成了!
> check(getwd())
Updating gridgame documentation
Loading gridgame
Writing NAMESPACE
Writing G2048-class.Rd
Writing g2048.Rd
Writing Game-class.Rd
Writing Snake-class.Rd
Writing snake.Rd
"C:/PROGRA~1/R/R-30~1.3/bin/x64/R" --vanilla CMD build \
"D:\workspace\R\app\gridgame" --no-manual --no-resave-data
* checking for file 'D:\workspace\R\app\gridgame/DESCRIPTION' ... OK
* preparing 'gridgame':
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files
* checking for empty or unneeded directories
* building 'gridgame_0.0.1.tar.gz'
"C:/PROGRA~1/R/R-30~1.3/bin/x64/R" --vanilla CMD check \
"C:\Users\ADMINI~1\AppData\Local\Temp\RtmponOeAc/gridgame_0.0.1.tar.gz" \
--timings
* using log directory 'C:/Users/ADMINI~1/AppData/Local/Temp/RtmponOeAc/gridgame.Rcheck'
* using R version 3.0.3 (2014-03-06)
* using platform: x86_64-w64-mingw32 (64-bit)
* using session charset: ASCII
* checking for file 'gridgame/DESCRIPTION' ... OK
* checking extension type ... Package
* this is package 'gridgame' version '0.0.1'
* checking package namespace information ... OK
* checking package dependencies ... OK
* checking if this is a source package ... OK
* checking if there is a namespace ... OK
* checking for executable files ... OK
* checking for hidden files and directories ... OK
* checking for portable file names ... OK
* checking whether package 'gridgame' can be installed ... OK
* checking installed package size ... OK
* checking package directory ... OK
* checking DESCRIPTION meta-information ... OK
* checking top-level files ... OK
* checking for left-over files ... OK
* checking index information ... OK
* checking package subdirectories ... OK
* checking R files for non-ASCII characters ... OK
* checking R files for syntax errors ... OK
* checking whether the package can be loaded ... OK
* checking whether the package can be loaded with stated dependencies ... OK
* checking whether the package can be unloaded cleanly ... OK
* checking whether the namespace can be loaded with stated dependencies ... OK
* checking whether the namespace can be unloaded cleanly ... OK
* checking loading without being on the library search path ... OK
* checking dependencies in R code ... OK
* checking S3 generic/method consistency ... OK
* checking replacement functions ... OK
* checking foreign function calls ... OK
* checking R code for possible problems ... OK
* checking Rd files ... OK
* checking Rd metadata ... OK
* checking Rd cross-references ... OK
* checking for missing documentation entries ... OK
* checking for code/documentation mismatches ... OK
* checking Rd \usage sections ... OK
* checking Rd contents ... OK
* checking for unstated dependencies in examples ... OK
* checking examples ... OK
* checking PDF version of manual ... OK
在执行check过程中,你的项目里可能会有其他的文件,检查也过不去的。你可新建一个文件.Rbuildignore,通过这个文件配置,可以忽略不希望参与打包的文件。
~ vi .Rbuildignore
.gitignore
dist
^.*\.Rproj$
^\.Rproj\.user$
README*
NEWS*
FAQ*
这样一些帮助文件,就能躲避检查了。
2.7 程序打包
在检查通过以后,我们就可以自由地打包了,用build命令。
我们可以选择2种打包方式,源代码打包和二进打包。
默认是给源代码打包。
> build()
"C:/PROGRA~1/R/R-30~1.3/bin/x64/R" --vanilla CMD build \
"D:\workspace\R\app\gridgame" --no-manual --no-resave-data
* checking for file 'D:\workspace\R\app\gridgame/DESCRIPTION' ... OK
* preparing 'gridgame':
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files
* checking for empty or unneeded directories
* building 'gridgame_0.0.2.tar.gz'
[1] "D:/workspace/R/app/gridgame_0.0.2.tar.gz"
二进制打包
> build(binary=TRUE)
"C:/PROGRA~1/R/R-30~1.3/bin/x64/R" --vanilla CMD INSTALL \
"D:\workspace\R\app\gridgame" --build
* installing to library 'C:/Users/Administrator/AppData/Local/Temp/RtmpI3hhpp'
* installing *source* package 'gridgame' ...
** R
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
*** arch - i386
*** arch - x64
* MD5 sums
packaged installation of 'gridgame' as gridgame_0.0.2.zip
* DONE (gridgame)
[1] "D:/workspace/R/app/gridgame_0.0.2.zip"
这两个文件都可以用出来发布项目,用户下载后可以直接进行安装。
# 安装命令
~ R CMD INSTALL gridgame_0.0.2.tar.gz
* installing to library 'C:/Users/Administrator/R/win-library/3.0'
* installing *source* package 'gridgame' ...
** R
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
*** arch - i386
*** arch - x64
* DONE (gridgame)
3. gridgame包发布
最后一步,就是把我的好不容易开发的包,发布到资源库。有3个地方可以发布。
- CRAN:R的官方发布平台
- R-Forge:R-Forge发布平台
- RForge:RForge发布平台
- Github:个人开源发布平台
3.1 Github:个人的开源发布平台
在Github上发布是最容易的,只要把项目代码上传到Github就完成了,都不需要做check()检查。基于Github的包管理工具是devtools包,我把gridgame项目已上传到Github, 项目地址是:https://github.com/bsspirit/gridgame,用户可以下面两种方式,直接从Github安装gridgame项目。
方法一:使用devtools包,二进制安装。
library(devtools)
install_github("gridgame","bsspirit")
方法二:通过源代码安装。
git clone https://github.com/bsspirit/gridgame.git
R CMD BUILD gridgame
R CMD INSTALL gridgame_*.tar.gz
3.2 R-Forge:R-Forge发布平台
在R-Forge(https://r-forge.r-project.org/)发布,就比较麻烦了,你需要先注册一个账号,https://r-forge.r-project.org/account/register.php,登陆后,再新建一个项目,需要等72小时审核才能通过。
然后,通过SVN把项目的源代码提交上去。我用习惯了Git进行版本管理,再用回SVN感觉好老土啊!
在RForge提交代码,并运行通过以后,你会有项目介绍页:http://gridgame.r-forge.r-project.org/,别个会看到介绍,下载你的包。
用户可以直接查看项目信息 http://gridgame.r-forge.r-project.org/,也可以在线查看项目源代码,https://r-forge.r-project.org/scm/viewvc.php/?root=gridgame,R-Froge平台会每天自动打包一次。
3.3 RForge:RForge发布平台
此RForge(http://rforge.net/)非R-Froge,竟然两个名字如此之近似,第一次用的人肯定会混的。首先注册RForge账号,同时注册一个要发布的项目。gridgame项目,我已经上传到Github了,这边能直接导入Github项目,就非常方便了。
通过RForge源下载gridgame包,可以直接用install.packages()函数。
install.packages('gridgame', repos = 'http://rforge.net') # 未发布成功,请先用Github的方案
3.4 CRAN发布:R的官方发布平台
这4个发布平台,CRAN是最全威的、是官方的,也不是那么容易发布的,有很严格的审查机制。CRAN发布条款:http://cran.r-project.org/web/packages/policies.html
我们明白政策后,通过 http://cran.r-project.org/submit.html 提交项目,大概要等待48小时审查。可能我这个包的问题比较严重6个小时内有就了回复。
- 第一次不合格:没有标出只支持Window平台,对应DESCRIPTION文件中OS_type: windows。(当然,他是不会告诉你怎么改的,找自己Google找)
- 第二次不合格:Linux平台R CMD check出错。(加了OS_type后,Linux执行当然会出错了,老外似乎也晕了。)
- 第三次不合格:为什么Linux不能用,为什么用.Platform$OS.type的代码检查,getGraphicsEvent在没有GUI的环境中怎么办,文档不全,对game framework的定义不清楚。(费了好大劲的解释,把这几篇文章的设计理念,写了封总结的邮件。)
- 第四次不合格:这次Uwe Ligges的态度很强硬,必须把Rd写完整,必须支持至少2个平台,必须对getGraphicsEvent进行检查,必须处理OS.type的代码问题,没有商量的余地,不搞定就不发包。(我真是悲剧了,看来发布项目,又要延迟一周了。)
下面是,提交项目到CRAN过程。
第二步:核对DESCRIPTION文件中的描述,与网页自动解析的内容是否一致。
第一次不合格,老外回复的邮件:
On 25/07/2014 04:24, Dan Zhang wrote:
> [This was generated from CRAN.R-project.org/submit.html]
>
> The following package was uploaded to CRAN:
> ===========================================
>
> Package Information:
> Package: gridgame
> Version: 0.0.1
> Title: A game framework for R
> Author(s): Dan Zhang [aut, cre]
> Maintainer: Dan Zhang
> Depends: R (>= 3.0.1)
> Description: This package provides a general-purpose game framework for
'This package provides' is redundant.
> grid game in R. The package includes 2 games about snake and
> 2048. You can run the function snake() or g2048() to startup
> the game. These games are only running on Window platform.
Eh? The CRAN policies do not allow such a package, and you have not
marked this as Windows-only.
> License: GPL-3
> Imports: methods
>
>
> The maintainer confirms that he or she
> has read and agrees to the CRAN policies.
>
> Submitter's comment: This package provides a general-purpose game
> framework for grid game in R. The package includes 2
> games about snake and 2048. You can run the function
> snake() or g2048() to startup the game. These games
> are only running on Window platform.
>
--
Brian D. Ripley, ripley@stats.ox.ac.uk
Professor of Applied Statistics, http://www.stats.ox.ac.uk/~ripley/
University of Oxford, Tel: +44 1865 272861 (self)
1 South Parks Road, +44 1865 272866 (PA)
Oxford OX1 3TG, UK Fax: +44 1865 272595
经过多次的对决和修改,终于把包成功发布到了CRAN。
同学们可以直接下载使用了。
# 从CRAN下载gridgame包
install.packages('gridgame') # 未发布成功,请先用Github的方案
# 加载gridgame包
library(gridgame)
# 启动贪食蛇游戏
snake()
# 启动2048游戏
g2048()
到CRAN上发布一个R包,真是不一件轻松的事情啊。坚持,修改,打磨,再坚持,虽然过程很痛苦,但是软件质量最终得到了保证,这就是CRAN严格审查的意义。
转载请注明出处:
http://blog.fens.me/r-game-gridgame/
赞,楼主实在太有毅力了,看来我离发布R包的梦想还差的很远
在github上发布很容易的,发布CRAN到确实比较头疼,还没有审核通过呢。
[…] R6是一个单独的R包,与我们熟悉的原生的面向对象系统类型S3,S4和RC类型不一样。在R语言的面向对象系统中,R6类型与RC类型是比较相似的,但R6并不基于S4的对象系统,因此我们在用R6类型开发R包的时候,不用依赖于methods包,而用RC类型开发R包的时候则必须设置methods包的依赖,在发布gridgame游戏包 文章中,就会出现RC依赖于methods包的使用情况。 […]