五子棋课程大纲.docx
- 文档编号:4916840
- 上传时间:2022-12-11
- 格式:DOCX
- 页数:48
- 大小:2.29MB
五子棋课程大纲.docx
《五子棋课程大纲.docx》由会员分享,可在线阅读,更多相关《五子棋课程大纲.docx(48页珍藏版)》请在冰豆网上搜索。
五子棋课程大纲
五子棋项目课程大纲
1课程目的
●应用为主,运用所学知识解决具体问题;
●了解“项目”的含义、要素及开发流程;
●体会软件工程的意义。
2课程目标
基本目标:
●熟练掌握文件I/O和设备I/O操作;
●掌握Framebuffer、鼠标操作原理;
●熟练掌握网络编程的模型、流程和有关函数。
利用上述知识,完成3种版本的五子棋游戏。
●版本1:
本机人人对战,棋盘用字符模拟,棋子位置由键盘输入。
●版本2:
本机人人对战,棋盘用图形模式,棋子位置由鼠标输入。
●版本3:
网络方式人人对战,棋盘用字符模拟或图形模式,棋子位置由键盘或鼠标输入。
可选提高版本:
●以上3种模式下具备悔棋、复盘功能;
●以上3种模式下的人机对战。
棋盘样图:
3项目的概念和要素
项目的定义
●根据美国项目管理协会(PMI)的定义:
“项目”是为完成某一独特的产品或服务所做的一次性努力。
从根本上说,项目就是一系列的相关工作。
●中国项目管理研究委员会(PMRC)对项目的定义是:
“项目”是一个特殊的将被完成的有限任务。
它是在一定时间内,满足一系列特定目标的多项相关工作的总称。
项目的特征
●项目由一串相关任务所组成;
●项目需要资源;
●项目具有明确的目标;
●项目具有生命周期;
●项目是独一无二的;
●每个项目都有客户。
项目要素
●一个项目涉及的因素很多,关键有三个:
时间、资源、范围。
●一句话:
在有限的时间内,利用有限的资源达到一定的目标或实现特定的功能。
题外话:
关于项目经验,不要把概念定义得太狭隘。
我们想项目经验时,不必非要完成多么大的项目,也不必离开学校去全职上班。
了解项目的流程,亲自走过一遍,有过思考和总结,都叫做我们有项目经验。
即使在大学“疯”玩了4年,我们其实仍然拥有不少获得项目经验的机会。
至少有3个机会:
课程作业、假期实习、毕业论文。
以上机会都能让我们感受“项目”的流程和意义。
即使是小小的课程作业,用心做、做得精彩,都可以在求职面试时给我们自己加分。
以计算机专业同学为例,老师布置作业等同于工作分配,我们理解问题等同于需求分析,开始写等同于编码,交作业之前的检查等同于测试,交了之后等老师打分类似验收测试。
只要认真,就会有收获。
请务必在大学期间至少完成一个自己满意的项目,让自己真正全面掌握这个项目,让自己有所收获与成长。
摘自《程序员羊皮卷》一书
4软件工程基本观念
软件工程的目标是提高软件的质量与生产率,最终实现软件的工业化生产。
质量是软件需求方最关心的问题,用户即使不图物美价廉,也要求个货真价实。
生产率是软件供应方最关心的问题,老板和员工都想用更少的时间挣更多的钱。
软件的质量因素很多,如正确性,性能、可靠性、容错性、易用性、灵活性、可扩充性、可理解性、可维护性等等。
软件工程模型建议用一定的流程将各个环节连接起来,并可用规范的方式操作全过程,如同工厂的生产线。
常见的软件工程模型有:
线性模型(图1.2),渐增式模型(图1.3),螺旋模型,快速原型模型,形式化描述模型等等[Pressmam1999,Sommerville1992]。
人们都有自己的世界观和方法论,能自然而然地运用于生活和工作中。
同样,程序员脑子里的软件工程观念会无形地支配其怎么去做事情。
软件工程三十年的发展,已经积累了相当多的方法,但这些方法不是严密的理论。
实践人员不应该教条地套用方法,更重要的是学会“选择合适的方法”和“产生新方法”。
有谋略才会有好的战术。
几千年前,我们的祖先就在打闹之际写下了很多心得体会,被现代人很好地运用于工业和商业。
本节讲述软件开发中的三种基本策略:
“复用”、“分而治之”、“优化-折衷”。
4.1复用
复用就是指“利用现成的东西”,文人称之为“拿来主义”。
被复用的对象可以是有形的物体,也可以是无形的成果。
复用不是人类懒惰的表现而是智慧的表现。
因为人类总是在继承了前人的成果,不断加以利用、改进或创新后才会进步。
复用的内涵包括了提高质量与生产率两者。
由经验可知,在一个新系统中,大部分的内容是成熟的,只有小部分内容是创新的。
一般地可以相信成熟的东西总是比较可靠的(即具有高质量),而大量成熟的工作可以通过复用来快速实现(即具有高生产率)。
勤劳并且聪明的人们应该把大部分的时间用在小比例的创新工作上,而把小部分的时间用在大比例的成熟工作中,这样才能把工作做得又快又好。
把复用的思想用于软件开发,称为软件复用。
将具有一定集成度并可以重复使用的软件组成单元称为软构件(SoftwareComponent)。
软件复用可以表述为:
构造新的软件系统可以不必每次从零做起,直接使用已有的软构件,即可组装(或加以合理修改)成新的系统。
复用方法合理化并简化了软件开发过程,减少了总的开发工作量与维护代价,既降低了软件的成本又提高了生产率。
另一方面,由于软构件是经过反复使用验证的,自身具有较高的质量。
因此由软构件组成的新系统也具有较高的质量。
利用软构件生产应用软件的过程如图1.5所示。
软件复用不仅要使自己拿来方便,还要让别人拿去方便,是“拿来拿去主义”。
面向对象方法,Microsoft公司的COM规范[Rogerson1999],都能很好地用于实现大规模的软件复用。
用构
件建
造新
软件
定义
所需
构件
集合
应用
软件
系统
分解
提取构件
存在
创建新构件
构件不存在
图1.5利用软构件生产应用软件的过程
4.2分而治之
分而治之是指把一个复杂的问题分解成若干个简单的问题,然后逐个解决。
这种朴素的思想来源于人们生活与工作的经验,完全适合于技术领域。
软件人员在执行分而治之的时候,应该着重考虑:
复杂问题分解后,每个问题能否用程序实现?
所有程序最终能否集成为一个软件系统并有效解决原始的复杂问题?
软件系统
复杂问题
解决原始问题
程序1
子问题1
分解集成
程序2
子问题2
程序n
子问题n
图1.6软件领域的分而治之策略
图1.6表示了软件领域的分而治之策略。
诸如软件的体系结构设计、模块化设计都是分而治之的具体表现。
软件的分而治之不可以“硬分硬治”。
不像为了吃一个西瓜或是一只鸡,挥刀斩成n块,再把每块塞进嘴里粉碎搅拌,然后交由胃肠来消化吸收,象征复杂问题的西瓜或是鸡也就此消失了。
4.3优化-折衷
软件的优化是指优化软件的各个质量因素,如提高运行速度,提高对内存资源的利用率,使用户界面更加友好,使三维图形的真实感更强等等。
想做好优化工作,首先要让开发人员都有正确的认识:
优化工作不是可有可无的事情,而是必须要做的事情。
当优化工作成为一种责任时,程序员才会不断改进软件中的算法,数据结构和程序组织,从而提高软件质量。
优化工作的复杂之处是很多目标存在千丝万缕的关系,可谓数不清理还乱。
当不能够使所有的目标都得到优化时,就需要“折衷”策略。
软件中的“折衷”策略是指通过协调各个质量因素,实现整体质量的最优。
软件折衷的重要原则是不能使某一方损失关键的职能,更不可以象“舍鱼而取熊掌”那样抛弃一方。
例如3D动画软件的瓶颈通常是速度,但如果为了提高速度而在程序中取消光照明计算,那么场景就会丧失真实感,3D动画也就不再有意义了。
人都有惰性,如果允许滥用折衷的话,那么一当碰到困难,人们就会用拆东墙补西墙的方式去折衷,不再下苦功去做有意义的优化。
所以我们有必要为折衷制定严正的立场:
在保证其它因素不差的前提下,使某些因素变得更好。
以上3节内容摘自林锐博士的《软件工程思想》一书
5五子棋项目分析
5.1问题描述:
黑子和白子依次按照顺序落子,每一方落子之后,进行相应的判断,在此子的八个方向上有没有连续的五个同色棋子,如果有则此方获胜,游戏结束;如果没有,则对方继续落子,直到一方获胜为止。
5.2设计
可以采用自顶向下的模块化方法来设计这个程序。
不难看出,这个程序可以划分为4个部分:
输入棋子位置信息、棋盘显示、胜负判断、下棋流程控制,每个部分可以专门用一个程序模块来实现。
第5个模块显示欢迎信息、软件名称及作者信息,这个模块与其他4个模块之间没有任何直接关系,主要用于增强程序界面的友好性。
胜负判断模块不会直接与用户打交道,因此不必提供帮助机制。
其他4个模块都会与用户进行交互,因此应该多花一些精力来设计它们的用户接口,提供一些必要的帮助信息,增强程序界面的友好性。
除了前面提到的5个程序模块,实际上还需要一个根模块来调用这5个模块,根模块通常被编写成一个main函数。
按照“增量渐进式”开发思路,逐步分解各个模块。
输入棋子位置信息:
键盘输入棋子坐标、通过网络获取棋子位置信息、鼠标输入棋子坐标。
棋盘显示:
字符模拟、图形显示。
下棋流程控制:
该谁走棋、是否有一方获胜、是否悔棋、计时等。
从简单到复杂,可以产生3种版本的程序:
●版本1:
本机人人对战,棋盘用字符模拟,棋子位置由键盘输入;
●版本2:
本机人人对战,棋盘用图形模式,棋子位置由鼠标输入;
●版本3:
网络方式人人对战,棋盘用字符模拟或图形模式,棋子位置由键盘或鼠标输入。
5.3编码实现
●创建工作目录
●创建Makefile文件
●每个模块创建一个c文件
●必要时建立一个公共h文件
6Linux内核和输入输出
6.1内核结构框图
6.2文件系统
在Linux操作系统中,所有的外围设备(包括键盘、显示器)都被看作是文件系统中的文件,因此,所有的输入、输出都要通过读文件或写文件完成。
也就是说,通过一个单一的接口就可以处理外围设备和程序之间的所有通信。
通常情况下,在读或写文件之前,必须先将这个意图通知系统,该过程称为打开文件。
如果是写一个文件,则可能需要先创建该文件,也可能需要丢弃该文件中原先已存在的内容。
如果一切正常,操作系统将向程序返回一个小的非负整数,该整数称为文件描述符。
任何时候对文件的输入、输出都是通过文件描述符标识文件,而不是通过文件名标识文件。
系统负责维护已打开文件的所有信息,用户程序只能通过文件描述符引用文件。
设备文件不含有数据,它们的作用是映射物理设备至文件系统中的文件名。
设备文件通常存放在/dev或其子目录中。
6.3输入输出基本概念
任何一种操作系统中,程序在开始读写一个文件的内容之前,必须首先在程序与该文件之间建立连接或通信通道,这一过程称为打开文件。
Linux系统中有两种机制用于描述程序与文件的这种连接:
一种称为“文件描述字”,另一种称为“流”。
因此,系统中关于I/O的函数也分为两大类:
一类针对文件描述字操作,一类针对流操作。
当调用某个函数打开一个文件时,它要么返回一个文件描述字,要么返回一个流。
为了告诉系统要对哪个文件进行操作,必须将这两种数据对象之一作为参数传递给完成实际读写操作的函数。
完成对文件的读写之后,可以通过关闭文件来终止程序与文件的这种连接。
一旦关闭了一个文件描述字或者一个流,就不能再对它进行输入输出。
6.4文件描述字与流
Linux系统中,文件描述字表示为int类型的对象,流表示为指向一个结构的指针,即FILE*,因此流也称为文件指针。
文件描述字函数提供的是初等的、低级的输入输出操作接口。
文件描述字和流除了能表示程序与普通文件的连接外,也能表示与设备(如终端)、管道或套接字的连接,但是,如果需要对特定设备进行控制操作,则必须使用文件描述字,没有函数能够对流进行这类操作。
流给程序提供了更高一级的输入输出接口,它处于低级的文件描述字的上层,也就是说,它们是通过文件描述字函数实现的。
一般情况下,应当坚持使用流而不是文件描述字,除非想做某种特殊操作,而此操作只能用文件描述字才能完成。
6.5流和FILE对象
由于历史的原因,表示流的C数据结构类型是FILE而不是英文stream,FILE类型说明在头文件
用标准I/O函数fopen打开或创建一个流时,该函数便返回一个指向FILE结构的指针,此时称在程序和该文件之间建立了一个流。
Linux系统中每一个进程都有3个预先定义并自动打开的流,分别是:
stdin、stdout和stderr,分别代表标准输入、标准输出和错误输出。
一旦打开了一个流,就能对流进行读写。
有3中类型的函数:
1、字符I/O函数。
这种函数每次读或些一个字符,由标准I/O库函数来处理缓冲。
intfgetc(FILE*stream);
intgetc(FILE*stream);
intgetchar(void);
intfputc(intc,FILE*stream);
intputc(intc,FILE*stream);
intputchar(intc);
2、行I/O函数。
如,fgets、fputs,每次读写一行,每一行以换行符终止。
char*fgets(char*s,intcount,FILE*stream);
char*gets(char*s);
intfputs(constchar*s,FILE*stream);
intputs(constchar*s);
3、块I/O函数。
如,fread、fwrite,每次读写若干个对象,每个对象的大小是指定的。
这两个函数通常用于读写二进制文件,有时也称为二进制I/O、对象I/O或结构I/O。
size_tfread(void*data,size_tsize,size_tcount,FILE*stream);
size_tfwrite(constvoid*data,size_tsize,size_tcount,FILE*stream);
写到一个流的字符并不是一旦执行输出函数就立即写到文件中,而是先在一个缓冲区中聚集为一块,然后异步地以块为单位传送到文件中。
类此地,从一个流读出字符也不是一个一个地从文件中读出的,而是以块为单位从文件中读出的,这种处理方式称为“缓冲”。
标准I/O库函数自动为我们管理I/O缓冲区,但标准I/O库中最让人感到迷惑、最常见的一些问题常常是由缓冲区引起的。
格式化输出函数:
intprintf(constchar*format,…);
intfprintf(FILE*stream,constchar*format,…);
intsprintf(char*buf,constchar*format,…);
printf输出到标准输出流stdout;fprintf输出到参数stream指定的流;sprintf不是输出到一个文件,而是输出格式化的字符到参数buf所指的字符数组中,并且在buf的末尾自动添加一个空字节。
格式化输入函数:
intscanf(constchar*format,…);
intfscanf(FILE*stream,constchar*format,…);
intsscanf(char*buf,constchar*format,…);
scanf从标准输入流stdin读数据;fscanf从stream指定的流读数据;sscanf不是从文件而是从参数buf指定的字符数组中读数据。
每一个函数读取若干字节,根据format参数解释它们并存储结果到对应的参数中。
6.6文件描述字
对于Linux内核而言,所有打开的文件都通过文件描述字来引用。
文件描述字是一个非负整数,表示为int类型的对象。
同标准I/O情形一样,系统对每一个进程也预定义了3个文件描述字0、1和2,分别对应标准输入、标准输出和标准错误输出。
这3个文件描述字在头文件
STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO。
调用open函数返回一个文件描述字,文件描述字上的基本输入输出操作函数是read和write。
6.7阻塞的概念
阻塞的含义是,调用必须等待操作完成,即读写到数据,才能返回。
例如,read、write调用。
默认情况下,关于I/O的系统调用总是阻塞的。
对于输入操作,Linux实现输入操作通常有两个分开的步骤:
●等待数据到达
●从内核复制数据到进程
例如,对于来自终端的标准输入,第一个步骤等待终端上的数据到达,当终端键入了输入时,它被复制到内核的缓冲区中。
第二个步骤是将数据从内核缓冲区复制到应用程序的缓冲区。
如果第一个步骤完成,即数据已被复制到内核,我们称数据到达或数据可读;对描述字而言,则称描述字就绪可读。
如果此时调用read,read将立即读到数据,因而不会被阻塞。
使用最普遍的I/O方式是阻塞I/O,下图是用read读标准输入的例子。
进程调用read,read一直要等数据到达并且将它复制都进程提供的缓冲区之后,或者发生了错误时才会返回。
最常见的错误是调用被信号中断。
我们称进程在调用read直到返回的整个期间被阻塞。
当read返回OK时,应用进程才能继续运行。
通过在打开文件时指定O_NONBLOCK标志,或者对已经打开的文件描述字调用fcntl函数设置O_NONBLOCK标志,我们可以告诉内核:
“当请求的I/O操作暂时不能完成时,不要置进程于睡眠状态,而是立即返回并报告错误”。
如下图所示。
上图中,前3次read调用,因数据未就绪而立即返回,同时设置EWOULDBLOCK错误指出没有数据就绪。
第4次调用read时输入数据就绪,当数据被复制到用户缓冲区后,read返回OK,进程可以开始处理数据。
在复制数据到用户缓冲区期间,进程被阻塞。
上述方式称为“轮询”,应用程序连续地询问内核以查看描述字是否就绪。
缺点是浪费CPU时间,某些应用中偶尔也会使用。
6.8错误处理
Linux系统中,如果一个系统调用或库函数调用失败,会返回一个值-1,这个返回值仅仅告诉调用遇到了错误而已,为了知道究竟发生了什么错误,需要查看存储在外部变量errno中的错误码。
头文件
变量errno是一个外部整型变量,它的说明为:
externinterrno。
errno的值只有在函数调用出错时才被设置,在一次成功调用函数之后errno的值不会改变,它可能是前一个调用某个函数出错时的值。
因此,不能用errno来检测一个调用是否失败,正确的做法是仅当函数的返回值指出这个变量被明显设置时才用errno确定具体的错误原因。
错误码是一个整型值,C标准定义了两个函数帮助我们打印出错误码对应的标准出错信息。
#include
char*strerror(interrnum);
strerror映射由errnum指明的错误码到描述错误信息的字符串,其返回值是指向这个字符串的指针。
errnum的值通常来自于变量errno。
#include
voidperror(constchar*msg);
perror打印errno当前值对应的错误信息到标准错误输出流,它首先打印msg指定的信息,后随一个冒号和空格,然后打印与errno对应的错误字符串。
如果msg是空指针或指向空字符串,perror打印与strerror完全相同的信息,唯一区别是,perror附加有换行符,而strerror没有。
7Framebuffer原理
7.1内核空间和用户空间
Linux虚拟内存的大小为2^32(在32位机器上),内核将这4G字节的空间分为两部分。
最高的1G字节(从虚地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。
而较低的3G字节(从虚地址0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间”。
因为每个进程可以通过系统调用进入内核,因此,Linux内核空间由系统内的所有进程共享。
于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟地址空间(也叫虚拟内存)。
内核空间对所有的进程都是共享的,其中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据,不管是内核程序还是用户程序,它们被编译和连接以后,所形成的指令和符号地址都是虚地址,而不是物理内存中的物理地址。
我们首先掌握Linux下进程地址空间的布局以及堆栈帧的结构,下图所示。
任何一个程序通常都包括代码段和数据段,这些代码和数据本身都是静态的。
程序要想运行,首先要由操作系统负责为其创建进程,并在进程的虚拟地址空间中为其代码段和数据段建立映射。
光有代码段和数据段是不够的,进程在运行过程中还要有其动态环境,其中最重要的就是堆栈。
Linux下进程的地址空间布局
7.2图形图像基本概念
一副完整的图片
局部放大后的效果
每帧图像是由若干“像素”组成的,通常用“水平分辨率”和“垂直分辨率”描述一帧(一副)图像包含的像素数。
水平分辨率是指图像每行的像素个数,而垂直分辨率则是指显示完整一帧图像时屏幕上出现的水平线的数量。
“像素”记录了图像的颜色信息,颜色的表达有多种不同的方式,每一种颜色系统所适合的用途都各不相同。
最基本的一种表达方式为RGB颜色空间。
RGB代表“红-绿-蓝,”它是相机传感器和计算机图形显示方面常用的一种颜色系统。
由于这三种原色相加起来可以形成白光,故可以通过将各原色按不同比例进行调和的办法来形成可见光谱区的大多数颜色。
RGB是所有其他颜色空间的基础,在计算机图形学中,它是颜色空间的首选。
显示中的每个像素实际上都有3个子像素,每个都包含红、绿和蓝色滤波器,即人眼可以将其分辨为单色的像素。
这3种最常见的配置中,要么是每个通道使用8bit来表示RGB(RGB888格式),要么是每通道6bit表示(RGB666格式),或者R和B每通道用5bit表示,G通道用6bit数据表示(RGB565格式)。
在这3种配置中,RGB888提供了最好的色彩清晰度。
这种格式总共有24bit的分辨率,可以提供超过1600万种色彩。
它为LCD TV等高性能应用提供所需要的高分辨率和精度。
RGB666格式在便携式电子产品中非常流行。
这种格式具有总共18bit的分辨率,可以提供262000种色彩。
不过由于其采用的18引脚(6+6+6)数据总线并不能很好地与16bit处理器数据通道相兼容,在工业上一种常见的折衷方法是,R和B通道5bit,G通道6bit(5+6+5=16bit总线),以此来实现与RGB666平板的连接。
这种情形具有很好的性能,因为在视觉上绿色是3种颜色中最重要的。
7.3Framebuffer原理
帧缓冲(Framebuffer)是Linux为显示设备提供的一个接口,是把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。
这种操作是抽象的、统一的,用户不必关心物理显存的位置、换页机制等等具体细节,这些都由Framebuffer设备驱动来完成。
帧缓冲驱动的应用广泛,在linux的桌面系统中,X-Window服务器就是利用帧缓冲进行窗口的绘制。
尤其是通过帧缓冲可显示汉字点阵,成为Linux汉化的唯一可行方案。
LinuxFramebuffer本质上只是提供了对图形设备的硬件抽象,在开发者看来,Framebuffer是一块显示缓存,往显示缓存中写入特定格式的数据就意味着向屏幕输出内容,所以说Framebuffer就是一块白板。
例如对于初始化为32位色的Framebuffer来说,Framebuffer中的4个字节代
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 五子棋 课程 大纲