编程 |《五子棋编程文档》
目录
想法
一直以为编程是一个人的事情,感觉程序的思想和整体的流程都在一个人的脑海中,无法共享和传递。但是一个好的程序,一个完整的项目都不是一个人努力的结果,是所有和我一样,有着对编程的热爱和怀揣梦想的程序员们一点一滴编写而成。其中不乏技术牛人和大神,但是他们并没有孤立起来,而是更加喜欢分享与讨论。
这一次希望可以通过多人协同编写一个小程序,来适应并锻炼自己学会分享、积极讨论、学习的能力。之所以选择五子棋这个小程序作为开端,是因为第一次多个人一起编写一个程序,大家都不会适应,甚至停滞不前,所以难度不应该太高,代码量少为特点。要以少写多说的方式来磨合大家,如果效果可以,后续可以逐步提升难度和代码量。
程序流程
伪代码
main()
{
while(1)
begin_game() // 开始游戏
}
begin_game() // 开始游戏
{
initialize() // 初始化
while(1)
key_input() // 按键输入
if xy:k?
interface() // 刷新界面
else
judge_conflict() // 判断占位
judge_win_los() // 判断输赢
interface() // 刷新界面
}
initialize() // 初始化
{
array[size] // 初始化棋子数组
cursor[size] // 初始化光标信息
interface() // 刷新界面
}
interface() // 刷新界面
{
if Y:N? // 判断占位 输出提示信息
print N
if Y:N? // 判断5连珠 输出提示信息
if 0:1? // 输出胜负提示信息
print 0
else
print 1
for(;;)
if array[0||1]?0:1 // 根据数组数据输出棋子排列
}
key_input() // 按键输入
{
getch(key)
switch(key)
case x return key // 按下方向键 返回按键值
case y return key
case x return key
case y return key
case k return key
}
judge_win_los() // 判断输赢
{
for(;;)
for(;;)
if array[5?]?Y:N // 判断是否有五子连珠情况
if array[0||1]?0:1 // 判断是黑子胜还是白子胜
}
judge_conflict() // 判断占位
{
for(;;)
for(;;)
if array[0:0?]?Y:N // 判断是否占位
}
模块化
模块划分
- 框架
- 初始化
- 输入
- 悔棋
- 判断胜负
- 判断占用
- 刷新界面
参数 接口
函数名
int start_game(); // 框架
int initialize(); // 初始化
int key_input(); // 输入
int refresh_the_interface(); // 刷新界面
int judge_conflict(); // 判断占用
int judge_win_los(); // 判断胜负
数组
int chess_array[15][15] = {0}; // 棋子排列 说明:0为无棋子,1为白棋,2为黑棋。
光标坐标
[7][7]
y x
y * 15 + x
----------------------
[7][7]
i j
i * 15 + j
----------------------
int coordinate_x = 7; // 横坐标 说明:光标位置信息,x y同为7,代表光标在棋盘中央。
int coordinate_y = 7; // 纵坐标
标识符
int placeholder = 0; // 占位标识符 说明:0为没有占用,1为占用。
int win_and_los = 0; // 五子连珠标识符 说明:0为没有出现五子连珠, 1为有五子连珠出现。
int black_white = 0; // 黑白棋交替标识符 说明:奇数为黑棋,偶数为白棋。
int key_direction = 0; // 按键标识符 说明:0为方向键,1为空格。
循环变量
int i = 0;
int j = 0;
int k = 0;
int l = 0;
编程规范
0.规范制定说明
本套C语言编程规范为提高代码质量、便于维护、协同编码、可移植等特点而编写。要求所有参与编码人员要严格遵循本编程规范。
参考文献:
- 华为C语言开发编程规范。
- 阿里巴巴Java开发手册。
1.标识符命名与定义
建议使用unix like风格给标识符命名,即单词用小写字母,每个单词直接用下划线“_”分割。
例如:text_mutex,test_one,kernel_text_address。
我们对标识符定义主要是为了让团队的代码看起来尽可能统一,有利于代码的后续阅读和修改。标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。尽可能给出描述性名称,不要节约空间,让别人很快理解你的代码更重要。
示例:
好的命名:
int error_number;
int number_of_completed_connection;
不好的命名:
int n;
int n_comp_conns;
原则:除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音。
建议:
- 尽量避免名字中出现数字编号,除非逻辑上的确需要编号。
- 代码中的命名均不能以下划线开始,也不能以下划线符号结束。
- 重构/修改部分代码时,应保持和原有代码的命名风格一致。
- 全局变量应增加“g_”前缀。
- 禁止使用单字母命名变量,但允许定义i、j、k、l作为局部循环变量。
- 常量和宏的定义应该全部大写,多个单词应使用“_”相隔。
- 严禁使用未经初始化的变量作为右值。
- 变量初始化必须赋予初值。
2.函数
函数设计的精髓:编写整洁函数,同时把代码有效组织起来。
整洁函数要求:代码简单直接、不隐藏设计者的意图、用干净利落的抽象和直截了当的控制语句将函数有机组织起来。
示例:
static void calibrate_run_measurement_overhead(void)
{
u64 T0, T1, delta, min_delta = 1000000000ULL;
int i;
for (i = 0; i < 10; i++)
{
T0 = get_nsecs();
burn_nsecs(0);
T1 = get_nsecs();
delta = T1-T0;
min_delta = min(min_delta, delta);
}
run_measurement_overhead = min_delta;
printf("run measurement overhead: %Ld nsecs\n", min_delta);
}
建议:
- 一个函数仅完成一个功能。
- 重复代码应该尽可能提炼成函数。
- 避免函数过长,新增函数不超过100行(非空非注释行)。
- 避免函数的代码块嵌套过深,新增函数的代码块嵌套不超过4层。
- 函数的参数个数不超过5个。
3.参数/接口
在开始编码之前,应该对各个模块的接口和传递的参数做详细的讨论并制定计划。
例如:编写1 - 100之间所有所有偶数和与奇数和的两个函数。
#include <stdio.h>
int even(int number_even[]);
int odd(int number_odd[]);
int main(){
int number[100] = {0}, *p, i;
int sum_odd = 0, sum_even = 0;
p = number;
for (i = 0; i < 100; i++){
number[i] = i + 1;
}
sum_even = even(p);
sum_odd = odd(p);
printf("odd = %d\neven = %d", sum_odd, sum_even);
system("pause>nul");
return 0;
}
int even(int *number_even){
int i = 0, sum_even = 0;
for (i = 0; i < 100; i++){
if (( number_even[i] % 2 ) == 0){
sum_even++;
}
}
return sum_even;
}
int odd(int *number_odd){
int i = 0, sum_odd = 0;
for (i = 0; i < 100; i++){
if (( number_odd[i] % 2 ) != 0){
sum_odd++;
}
}
return sum_odd;
}
对于数组的传递,是传值还是传址要做明确说明。
4.关于注释
因首次采用模块化多人协同编程,建议所有变量(包括全局变量)定义语句的上方使用单行注释加以阐述和说明此变量的作用,语言简洁、易懂。最好不要超过一行。
示例:
模块说明:
/*
* lib/prio_tree.c - priority search tree
*
* Copyright (C) 2004, Rajesh Venkatasubramanian <vrajesh@umich.edu>
*
* This file is released under the GPL v2.
*
* Based on the radix priority search tree proposed by Edward M. McCreight
* SIAM Journal of Computing, vol. 14, no.2, pages 257-276, May 1985
*
* 02Feb2004 Initial version
*/
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/prio_tree.h>
单行注释:
/*
* Maximum heap_index that can be stored in a PST with index_bits bits
*/
static inline unsigned long prio_tree_maxindex(unsigned int bits)
{
return index_bits_to_maxindex[bits - 1];
}
建议
- 对于每个模块应使用块注释在模块上方说明作用和功能,语言简洁、易懂。
- 模块应标明作者、创建时间、修改时间、修改内容、参考文献等相关信息。
- 建议不要在模块或函数内部加入过多的注释影响阅读。注释的作用是解释难以表达的抽象意图,而不是重复描述代码。
- 如模块或函数有重大重构或修改时应标明修改时间和修改内容。
- 建议注释应放在其代码上方相邻位置或右方,不可放在下面。如放于上方则需与其上面的代码用空行隔开,且与下方代码缩进相同。
5.排版与格式
程序的可读性和可维护性除了编码好坏外,还要很重要的因素是编码时的排版与格式。在此次协同编码过程中,也应该注意排版风格一致、美观。
示例:
int swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
struct scatterlist *sg;
int i;
BUG_ON(dir == DMA_NONE);
for_each_sg(sgl, sg, nelems, i)
{
phys_addr_t paddr = sg_phys(sg);
dma_addr_t dev_addr = phys_to_dma(hwdev, paddr);
if (swiotlb_force || !dma_capable(hwdev, dev_addr, sg->length))
{
void *map = map_single(hwdev, sg_phys(sg),sg->length, dir);
if (!map)
{
/* Don't panic here, we expect map_sg users to do proper error handling. */
swiotlb_full(hwdev, sg->length, dir, 0);
swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir,attrs);
sgl[0].dma_length = 0;
return 0;
}
sg->dma_address = swiotlb_virt_to_bus(hwdev, map);
}
else
{
sg->dma_address = dev_addr;
}
sg->dma_length = sg->length;
}
return nelems;
}
EXPORT_SYMBOL(swiotlb_map_sg_attrs);
建议:
- 程序块应采用缩进风格编写,每级缩进为1个tab(4个空格)。
- 建议相对独立的程序块之间、变量说明之后必须加空行。
- 多个短语句(包括赋值语句)不允许写在同一行内,即一行只写一条语句。
- if、for、do、while、case、switch、default等语句独占一行。
- 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。
- 注释符(包括„/‟„//‟„/‟)与注释内容之间要用一个空格进行分隔。
- 所有花括号必须独占一行,if语句即使一条表达式也要加花括号。
6.可移植性
为确保每个函数和模块有较高的可移植性,再此规定使用统一的平台Visual Studio 2013及以上版本。
参考样例
-
棋盘界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHlrTRot-1605602404693)(file:///E:/C/%E4%BA%94%E5%AD%90%E6%A3%8B/1.jpg?lastModify=1489205640)]
-
光标及棋子
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15ckag6A-1605602404695)(E:\C\五子棋\2.jpg)]
-
胜负检查 and 判断占用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IW1aO7cQ-1605602404697)(E:\C\五子棋\3.jpg)]