热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PHP开发框架YiiFramework教程(4)Hangman猜单词游戏实例

有了前面“Hello,World”的例子和对YiiFrameworkWeb应用基础的介绍,可以开始介绍一个简单而相对而有比较完整的Web应用-Hangman(猜单词游戏),这个例子是随Yii开发包发布的。通过这个例子可以了解开发Yii应用的基本步骤.

有了前面“Hello,World”的例子和对Yii Framework Web应用基础的介绍,可以开始介绍一个简单而相对而有比较完整的 Web应用-Hangman(猜单词游戏),这个例子是随Yii 开发包发布的。通过这个例子可以了解开发Yii应用的基本步骤.

说 起“Hangman”,让我想起80年代末期高中时在CPC464计算机上完过的“猜单词游戏”-Hangman,每猜错一次,就把一个小人离 绞刑架前进一步。当时DOS才刚刚出来:-)。

开发一个Web应用,首先是进行需求分析,这个不在本教程之内,但为完整 起见,还是把“猜单词游戏”的规则列在下面:

猜单词游戏(英文:Hangman,“上吊的人”之意)是一个双人游戏。一 位玩家想一个字,另一位尝试猜该玩家所想的字中的每一个字母。

要猜的字以一列横线表示,让玩家知道该字有多少个 字母。如果猜字的玩家猜中其中一个字母,另一位便须于该字母出现的所有位置上写上该字母。如果猜的字母没有于该字中出现 ,另一位玩家便会画吊颈公仔的其中一笔。游戏会在以下情况结束:

 

PHP开发框架Yii Framework教程(4) Hangman猜单词游戏实例 

PHP开发框架Yii Framework教程(4) Hangman猜单词游戏实例

PHP开发框架Yii Framework教程(4) Hangman猜单词游戏实例

“我要t字 。”“有, 在第八和第十一位。”

猜字的玩家猜完所有字母,或猜中整个字

另一位玩家画完整幅图:

今天给出的例子就不画出“上吊人”了,猜对了显示“You Win”,猜错了显示“You Lose”。 因此我们可以设计四个页面 :

这四个页面对应到Yii Framework为 四个View,可以分别取名为play, guess, win,lose ,每个页面都显示了 “Hangman Game”的标题,因此可以设计一个”MasterPage”,在Yii中成为Layout布局的模板以供四个View共享。Yii应用采用 了MVC设计模式,因此我们可以为四个View设计一个Controller–>GameController.

前面的教程说过Yii应用使用缺省 的目录结构来存放应用的不同部分,可以使用Yii提供的工具来参加一个缺省的项目目录。不过我个人还是比较喜欢自己创建各 个目录,因此根据上面的需求和界面设计,可以创建项目的目录结构如下:

创建的GameController.php 放在 protected/controller 目录下。

创建的四个View guess.php, lose.php, play.php, win.php 放在 protected/views/game 目录下 。目录名game 对应到所 使用到GameController.

创建的共享的Layout放在  protected/views/layout 目录下,缺省的布局名称为main.php

应用的配置文件放在 protected/config ,缺省配置文件为main.php

应用的入口脚本为 index.php

此外,供猜单词的文本文件为 word.txt

1. 首先来看看配置文件protected/config/main.php

return array(    
    'name'=>'Hangman Game',    
    'defaultController'=>'game',    
    'components'=>array(    
        'urlManager'=>array(    
            'urlFormat'=>'path',    
            'rules'=>array(    
                'game/guess/'=>'game/guess',    
            ),    
        ),    

    ),    
);CWebApplication应用的所有可写的属性都可以通过配置文件来定义,我们看到配置文件定义了应用的名称为”Hangman Game” ,然后修改Web应用缺省的Controller名字为game 对应到 GameController, 如果没有重新定义defaultController,则 缺省的Controller名字为SiteController,这样对于的View就要存放到 protected/views/site 目录下。另外这个Yii应用打开 了urlManager组件,这个组件的功能就在后面介绍,主要是用来定义用户可以访问的URL的格式(路由格式)。

2. 有了 这个配置文件,就可以在入口脚本中使用它,每个Yii应用的入口脚本index.php都是大同小异的,大部分情况下都是Copy & Paste

$yii=dirname(__FILE__).'/../../framework/yii.php';    
$cOnfig=dirname(__FILE__).'/protected/config/main.php';    
require_once($yii);    
Yii::createWebApplication($config)->run();3. 然后定义View使用的布局文件 protected/views/layout/main.php main.php 为缺省的布局模板,应用可以修改View使用的布局,本例就是要缺省的布局名称 main.

Transitional//EN"    
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">    
    
    
    
    
    

    
Hangman Game    

    

    

布局基本上就是HTML文件,其中 作为 view 的placeholder ,也就是在显示具体的View时,比如 play.php 就用 play.php 的内容来替代 $content。从而实现了类似“MasterPage” 的功能。 4. 下面就可以逐一定义四个View,这里就不一一列出了,以play.php 为例:

This is the game of

Hangman.    
You must guess a word, a letter at a time.    
If you make too many mistakes, you lose the game!

    
        
    
        
    
        

    
    
        
    
You must choose a difficulty level!    
    
        
可以看到基本上也是HTML ,其中CHtml 为Yii 框架支持的一个辅助类,用来 帮助生成HTML代码。 Hangman比较简单,因此没有使用单独的Model,而是通过render推送的方式传入参数。

需通过传递 视图的名称调用 CController::render()。这个方法将在 protected/views/ControllerID 目录下寻找对应的视图文件.

在视图脚本内部,我们可以通过 $this 来访问控制器实例.我们可以在视图里以 $this->propertyName 的方式 拉取 控制器 的任何属性.

我们也可以用以下 推送 的方式传递数据到视图里:

$this->render('edit', array(
    'var1'=>$value1,
    'var2'=>$value2,
));在以上的方式中, render() 方法将提取数组的第二个参数到变量里.其产生的结果是,在视图脚本里,我们可以直接 访问变量 $var1 和 $var2.

5.定义好布局和View之后,就可以写GameController了,

class GameController

extends CController    
{    
    /**   
     * @var string sets the default action to be 'play'   
     */
    public $defaultAction='play';    

    /**   
     * The 'play' action.   
     * In this action, users are asked to choose a difficulty level   
     * of the game.   
     */
    public function actionPlay()    
    {    
        ...
    }

    /**   
     * The 'guess' action.   
     * This action is invoked each time when the user makes a guess.   
     */
    public function actionGuess()    
    {    
        ...    
    }    

    /**   
     * The 'guess' action.   
     * This action is invoked when the user gives up the game.   
     */
    public function actionGiveup()
    {    
        ...
    }

    ...    
}一般情况下Controller缺省的action 为 index ,可以通过$defaultAction修改缺省的Action,本例修改为play. 因 此如果本例的 url 为 http://127.0.0.1:8888/yii/demos/hangman/ 那么使用 http://127.0.0.1:8888/yii/demos/hangman/index.php 和使用 http://127.0.0.1:8888/yii/demos/hangman/index.php?game/play的效果是一样的。缺省的Controller为 GameController,GameController缺省的action为play.

Action (动作),动作可以被定义为一个以 action 单词作为前 缀命名的方法。Hangman定义了三个action ,actionPlay ,actionGuess, actionGiveup ,GameController 其它方法和属性和生 成单词,判断是否猜对等为具体的游戏逻辑和Yii框架关系不大,就不介绍了。

6. 首先看看缺省的playAction ,这是用 户调用的缺省方法,也就是说当用户组地址栏输入http://127.0.0.1:8888/yii/demos/hangman/index.php (或 http://127.0.0.1:8888/yii/demos/hangman/index.php?game/play)所调用的Action。

public function

actionPlay()    
{    
    static $levels=array(    
        '10'=>'Easy game; you are allowed 10 misses.',    
        '5'=>'Medium game; you are allowed 5 misses.',    
        '3'=>'Hard game; you are allowed 3 misses.',    
    );    
        
    // if a difficulty level is correctly chosen    
    if(isset($_POST['level'])    
       && isset($levels[$_POST['level']]))    
    {    
        $this->word=$this->generateWord();    
        $this->guessWord=str_repeat('_',strlen($this->word));    
        $this->level=$_POST['level'];    
        $this->misses=0;    
        $this->setPageState('guessed',null);    
        // show the guess page    
        $this->render('guess');    
    }    
    else
    {    
        $params=array(    
            'levels'=>$levels,    
            // if this is a POST request,    
            //it means the level is not chosen    
            'error'=>Yii::app()->request->isPostRequest,    
        );    
        // show the difficulty level page    
        $this->render('play',$params);    
    }    
}

这个方法定义了游戏的三个难度等级$levels, 有两个分支,如果没有选择难易等级,则调用$this->render (‘play’,$params),显示Play页面,就$params (Array)推送到对应的View ,protected/views/play.php,参考上面View的 定义:

View使用Radiobutton来 显示 $levels 定义的列表。

如果用户选择了难易等级,在把Level,单词等存放到GameController所定义的属性中,如 word,level等。GameController拍手与CController 也是CComponent的子类,CComponent支持了类似C#,Java的属性功能。具 体后面再介绍。然后调用$this->render(‘guess’); 显示Guess页面。 Guess页面 guess.php 定义如下:

Please make a guess

    
        

    
  guessWord; ?>

    
        

You have made    
  misses; ?>    
  bad guesses out of a maximum of    
  level; ?>.

    
        
    
        

Guess:    
for($i=ord('A');$i<=ord('Z');++$i)    
{    
    if(!$this->isGuessed(chr($i)))    
        echo "\n".CHtml::linkButton(chr($i),    
        array('submit'=>array('guess','g'=>chr($i))));    
}    
?>    

    
        

array('submit'=>array('giveup'))); ?>

    
        
在View中可以直接通过$this 来访问对应的Controller实例对象的方法和属性。 如$this->guessWord,$this- >isGuessed(chr($i))等。其中点击26个字母触发guessAction (array(‘submit’=>array(‘guess’,'g’=>chr($i))))).

7. 下面为 guessAction 的定义

public function actionGuess()    
{    
    // check to see if the letter is guessed correctly    
    if(isset($_GET['g'][0]) && ($result=$this->guess($_GET['g'][0]))!==null)    
        $this->render($result ? 'win' : 'lose');    
    else // the letter is guessed correctly, but not win yet    
    {    
        $guessed=$this->getPageState('guessed',array());    
        $guessed[$_GET['g'][0]]=true;    
        $this->setPageState('guessed',$guessed,array());    
        $this->render('guess');    
    }    
}其中参数 ‘g’由 guess 页面提交是传入, 如果单词全部猜对在显示”You win” 或用完所有次数猜错显示“You lose” , $this->render($result ? ‘win’ : ‘lose’), 如果还有机会猜还是回到guess 页面$this->render(‘guess’);

8. 在Guess页面上还有一个“Give up” 按钮,用 户点击则触发giveupAction.这个方法比较简单,直接显示lose 页面

public function actionGiveup()    
{    
    $this->render('lose');    
}至此Hangman游戏基本就完成了。游戏虽然简单,但说明了使用Yii开发应用的基本流程,下面给出Yii开发文档给出 的开发流程,Hangman比较简单,没有用到数据库和国际化等。

此处的开发流程假设我们已经完成了对应用的需求分析和 必要的设计分析。

创建目录结构骨架。创建第一个Web应用 中讲到的 yiic工具可以快速实现此步骤。

配置此 应 用。这是通过修改应用配置文件实现的。 此步骤可能也需要编写一些应用组件(例如用户组件)。

为所管理的每个类型 的数据创建一个 模型 类。 Creating First Yii Application 和 Automatic Code Generation 中讲述的 Gii 工具可以用于快 速为每个数据表创建 active record 类。4.为每个类型的用户请求 创建一个 控制器 类。 具体如何对用户请求归类要看实际 需求。总体来说,如果一个模型类需要被用户访问,他就应该有一个相应的控制器类。 Gii 工具也可以自动实现这一步骤。

实现 动作 和他们相应的 视图。 这是真正所需要做的工作。

在控制器类中配置必要的动作 过滤器。

如 果需要主题功能,创建 主题 。

如果需要 国际化(I18N) ,创建翻译信息。

对可缓存的数据点和视图点应用适 当的 缓存 技术。

最终 调整 与部署。

上述的每个步骤中,可能需要创建并执行测试用例。



推荐阅读
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 导出功能protectedvoidbtnExport(objectsender,EventArgse){用来打开下载窗口stringfileName中 ... [详细]
  • 本文是关于C#类型系统、值类型和引用类型的概念性笔记。介绍了C#1系统类型的三个特性,静态类型的含义,显式类型和隐式类型的区别。还讨论了类、结构、数组类型、枚举、委托类型和接口类型属于哪一种类型。同时纠正了关于结构、引用类型和对象传递的错误表述。最后提到了C#4中使用动态类型的关键字。 ... [详细]
  • 在C#中,使用关键字abstract来定义抽象类和抽象方法。抽象类是一种不能被实例化的类,它只提供部分实现,但可以被其他类继承并创建实例。抽象类可以用于类、方法、属性、索引器和事件。在一个类声明中使用abstract表示该类倾向于作为其他类的基类成员被标识为抽象,或者被包含在一个抽象类中,必须由其派生类实现。本文介绍了C#中抽象类和抽象方法的基础知识,并提供了一个示例代码。 ... [详细]
  • 在C#/ .NET中,处理基督面前的日期没有内置支持,需要编写自己的代码。本文介绍了在处理此类日期时的最佳方法,并提供了一个示例链接,可以参考编码的解决方案。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • switch语句的一些用法及注意事项
    本文介绍了使用switch语句时的一些用法和注意事项,包括如何实现"fall through"、default语句的作用、在case语句中定义变量时可能出现的问题以及解决方法。同时也提到了C#严格控制switch分支不允许贯穿的规定。通过本文的介绍,读者可以更好地理解和使用switch语句。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
author-avatar
方雅俊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有