发布gridgame游戏包

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/

gridgame

前言

为了能发布自己的游戏包,我们已经储备了很多的基础知识,包括 R语言面向对象编程、R包开发编程、R语言游戏编程 等,最后一步就到综合运用的时候了。按CRAN的R包发布的要求,把所有代码和文档串起来,就是我们要发布的gridgame游戏包。

目录

  1. 知识储备
  2. gridgame包开发
  3. gridgame包发布

1. 知识储备

在综合运用所有知识前,先回顾一下,我们都需要掌握哪些知识,1)R语言编程的基础是必需,2)游戏的算法主要是对矩阵的操作,线性代数最好也要掌握,3)游戏操作需要有界面,虽然不需要太好看,但我们也要有能力用代码画出游戏界面来,4)游戏的框架封装,对于相同类型的游戏,如果第二款游戏能延续第一款游戏的结构,不仅能简化开发节约时间,还能降低游戏技术门槛,就需要用面向对象的思维模式对游戏框架进行封装。

1.1 基础知识

上面列出的4个主要知识点,我都做技术准备,针对不同问题,参考不同文章就可以解决:

1.2 给R包起名

技术准备一切就绪,先给项目起个名字吧!

其实,起名也是有讲究的,不能太随便,虽然CRAN上面没有叫game包,但我们直接用game做为项目名字,也不是很好。如何起名最好先问问Google,找一个没有太多重名的关键字作为名字。

直接用google搜索 game 有 1,100,000,000 条结果,r game 有1,680,000,000条结果。我连0都数不过来了!和这么多搜索结果去竞争关键字,是我们在推广过程中不可逾越的鸿沟。

rgame-google

所以,换个名字让我们从小成长,找一个热度不是那么高的关键字,作为项目名字。gridgame就是一个不错的选择,既能表现游戏的特征,从推广角度又没有特别强大的竞争对手,一下子压力全无,倍感轻松!

gridgame-google

做好了各种准备工作,下面就是把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小时审核才能通过。

在R-Forge中,gridgame项目管理界面截图
rfroge1

然后,通过SVN把项目的源代码提交上去。我用习惯了Git进行版本管理,再用回SVN感觉好老土啊!

在RForge提交代码,并运行通过以后,你会有项目介绍页:http://gridgame.r-forge.r-project.org/,别个会看到介绍,下载你的包。

rfroge2

用户可以直接查看项目信息 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项目,就非常方便了。

rfroge

通过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过程。

第一步:填写用户基本信息,并上传打好的tar.gz包
cran1

第二步:核对DESCRIPTION文件中的描述,与网页自动解析的内容是否一致。
cran2

第三步:等待审核。
cran3

第一次不合格,老外回复的邮件:


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/

打赏作者

This entry was posted in R语言实践, 游戏

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
joshuaas

赞,楼主实在太有毅力了,看来我离发布R包的梦想还差的很远

Conan Zhang

在github上发布很容易的,发布CRAN到确实比较头疼,还没有审核通过呢。

[…] R6是一个单独的R包,与我们熟悉的原生的面向对象系统类型S3,S4和RC类型不一样。在R语言的面向对象系统中,R6类型与RC类型是比较相似的,但R6并不基于S4的对象系统,因此我们在用R6类型开发R包的时候,不用依赖于methods包,而用RC类型开发R包的时候则必须设置methods包的依赖,在发布gridgame游戏包 文章中,就会出现RC依赖于methods包的使用情况。 […]

3
0
Would love your thoughts, please comment.x
()
x