├── .gitignore ├── README ├── inc ├── console.h ├── error.h ├── game.h ├── map.h ├── player.h ├── rich_client.h ├── rich_server.h ├── util.h └── wrap.h ├── net ├── Makefile ├── client.c ├── error.c ├── rich_client.c ├── rich_server.c ├── server.c ├── server_select.c └── wrap.c ├── src ├── Makefile ├── console.c ├── game.c ├── input.txt ├── map.c ├── out.txt ├── player.c ├── rich.c ├── test.c └── util.c └── test case └── case1.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /src/rich 2 | */*.out 3 | */*.bak 4 | /*.* 5 | !/.gitignore 6 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | /* linux终端下C语言编程练习 大富翁游戏 可参考rich_jpeg.jpg中的数据结构 2 | * 在src目录下执行make, 执行./rich 运行游戏 网络版运行方式:打开两个终端,在终端1 3 | * 下的src目录下执行./rich -s ,在终端2下的src目录下执行./rich -c,终端1首先运行,然后 4 | * 终端2运行。 5 | * 6 | * 7 | * V0.1 完成一位玩家的行走,在land中用symbol保存临时的显示符号 打印地图时首先判断 8 | * 有无临时显示的元素。2012.11.22 9 | * V0.2 修改update_map函数,完成4人切换显示 在地图上显示玩家符号颜色,用于每次刷 10 | * 新land中的显示符号,程序框架基本确定 2012.11.22 11 | * V0.3 添加查询玩家信息函数player_info, 添加game中id及player_num相关函数,修改 12 | * update_map中的玩家数量的bug, 添加buy_land函数。2012.11.23 13 | * V0.4 添加单一玩家退出函数player_quit(),修正打印玩家信息时需要的id为玩家自身id, 14 | * 修改了deinit_game()中i的范围bug。 15 | * V0.5 在land中添加土地所有者的颜色owner_color,修改init_land(),print_game_map() 16 | * 修正玩家退出后仍显示符号的bug,修改print_game_map()中land显示bug。添加主函 17 | * 数文件rich.c。修改print_game_map(),使其在终端的中间打印地图。2012.11.25 18 | * V0.6 重新在util中写了print_bar代替了welcome(). 删除main中多余代码。 19 | * V0.7 添加roll函数 20 | * V0.8 重写cmd_go中的if-else为switch,暂时没想到更好的方式处理,静态表或树结构? 21 | * 添加upgrade_house函数。简化main函数,好像没啥好处。添加print_p_info 可查看 22 | * 玩家拥有的土地数量。修改退出时的显示 23 | * V0.9 添加tool相关功能,为玩家添加tool字段,购买道具,修改print_p_info的bug,重写 24 | * init_land(); 2012.11.30 25 | * V1.0 添加道具使用相关结构,和cmd的执行类似 26 | * V1.1 添加路障Block的使用和遇到Block的操作2012.12.04 27 | * V1.2 修改print_color函数,见tuil.h。添加道具使用的空函数,防止段错误 28 | * V1.3 修改 window_col()函数中获取标准输出的终端宽度,以防止输入重定向后不能得到标准 29 | * 输入的宽度的bug,测试用的input.txt文件中,若单行只有一个命令,后面要跟个空格, 30 | * 否则读的命令不正确。测试方式 ./rich.out < input.txt 重定向输出会错误 同样是 31 | * window_col()引起的,可#undef LINUX_CONSOLE进行输出重定向,使用diff对测试结果比 32 | * 较。bug:Block不能在有玩家的地块上使用。 33 | * V1.4 重写six_rand()函数。 34 | * V1.5 添加网络相关函数,在/net目录下,./a.out是服务器,首先运行,然后分别在另外两个 35 | * 终端下执行/src目录下的./rich.out -n,实现两位玩家的互动 ,实现客户端主要修改的 36 | * 函数有:get_cmd_line();get_num();next_player(发送字符x);init();is_yes(); 37 | * 添加全局变量sockfd, game结构体中添加turn标志位代表是否轮到自己。2012.12.14 38 | * V1.6 重写makefile,以同时make程序和服务器,写法还是不标准。启动服务器的方式:在init 39 | * 函数中使用fork和信号。ParseCmdLine函数确定程序的启动方式: 40 | * -server 服务器 -clinet 客户端 默认本地运行 41 | * 在init中#define PLAYER_NUM 2设置玩家数量,同时修改rich_server.c中的wait_client(2); 42 | * 运行时终端数量也要对应增加 2012.12.21 43 | * V1.7 修改4人时退出的bug,在rich_server.c中为write函数添加pipe信号捕获函数write_err 44 | * ,防止在write错误后程序直接终止。 45 | * V1.8 添加一些注释 46 | * V1.9 将fork中的tell_parent挪至execl启动后的进程中,去掉延时。加入选择玩家数量功能。 47 | * V2.0 修复行走负步数跨原点S时越界,和block道具使用中的类似bug。2013.1.24 48 | * open_gg@qq.com 49 | */ -------------------------------------------------------------------------------- /inc/console.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSOLE_H 2 | #define CONSOLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include"game.h" 9 | #define LINE_MAX 256 10 | #define CMD_MAX 50 11 | #define LOCAL 0 12 | #define NET_SERVER 1 13 | #define NET_CLIENT 2 14 | 15 | struct cmd_tbl_s { 16 | char *name; /* Command Name */ 17 | int (*cmd)(int, char *[]); 18 | }; 19 | 20 | typedef struct cmd_tbl_s cmd_tbl_t; 21 | 22 | int ParseCmdLine( int argc , char *argv[] ); 23 | void playing(); 24 | int init(int); 25 | int cmd_help(int n, char *c[]); 26 | 27 | 28 | 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /inc/error.h: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_H 2 | #define ERROR_H 3 | void err_dump(const char *, ...); /* {App misc_source} */ 4 | void err_msg(const char *, ...); 5 | void err_quit(const char *, ...); 6 | void err_exit(int, const char *, ...); 7 | void err_ret(const char *, ...); 8 | void err_sys(const char *, ...); 9 | #endif 10 | -------------------------------------------------------------------------------- /inc/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | #include"map.h" 4 | #include"player.h" 5 | #include"util.h" 6 | #if 0 //测试小地图 7 | #ifdef WIDTH 8 | #undef WIDTH 9 | #define WIDTH WIDTH_S 10 | #endif 11 | #ifdef HEIGHT 12 | #undef HEIGHT 13 | #define HEIGHT HEIGHT_S 14 | #endif 15 | #endif 16 | #define LAND_NUM (WIDTH*2+(HEIGHT-2)*2) 17 | typedef struct land{ 18 | int x,y; //pos 19 | int price; //土地的价格 20 | st_player *p_player; //土地的所有者 21 | e_color owner_color; //土地所有者的颜色 22 | House_type level; //土地的等级 23 | e_color color; //土地的临时颜色 和站在上面的玩家颜色一致 24 | int tool_n; //土地上道具的数量 25 | int tools[MAX_TOOL_NUM]; //土地上的道具 26 | char symbol; //土地临时显示的符号 27 | 28 | }st_land_info; 29 | 30 | typedef struct game{ 31 | st_player *p_player_tab[MAX_PLAYER_NUM]; //玩家table 声明成指针还是结构体好呢? 32 | st_map_info *p_map; //地图指针 33 | st_land_info p_land[LAND_NUM]; //地块结构体 34 | int player_num; //玩家数 ==1则玩家胜利 游戏结束 35 | int id; //p_player_tab中的索引 指向当前玩家 36 | int my_turnflag; //网络版中的标志 1:my turn 0:others turn 37 | }st_game; 38 | 39 | #ifndef MAX_TOOL_NUM 40 | #define MAX_TOOL_NUM 10 41 | #endif 42 | #define SHOP 'H' 43 | typedef struct tool{ 44 | char symbol; //标志 45 | char *name; //名字 46 | int id; //id 47 | int price; //价格 48 | e_color color; //颜色 49 | void (*usetool)(st_game *,int); //使用道具 50 | void (*meettool)(st_game *,int); //在土地上遇到道具的效果 比如遇到路障 51 | }st_tool; 52 | 53 | 54 | /*使用静态表取代过多的if-else?*/ 55 | enum land_owner_type{Na=0,Owner,Other,Toolshop}; 56 | typedef enum land_owner_type e_LandOwnerType; 57 | 58 | #define MAX_LAND_FUN 50 59 | //land function table struct 60 | struct land_ftbl_s{ 61 | e_LandOwnerType owner_type; 62 | int (*f)(st_game *,int); 63 | }; 64 | typedef struct land_ftbl_s land_ftbl_t; 65 | 66 | //从game结构体中直接获取当前玩家的信息 67 | int get_p_money(st_game *p); 68 | int get_p_id(st_game *p); 69 | int get_p_step(st_game *p); 70 | void get_p_pos(st_game *p,int *x,int *y); 71 | void set_p_pos(st_game *p,int x,int y); 72 | void set_p_money(st_game *p,int money); 73 | char get_p_symbol(st_game *p); 74 | char *get_p_name(st_game *p); 75 | e_color get_p_color(st_game *p); 76 | void set_p_step(st_game *p,int step); 77 | 78 | //打印地图相关函数 79 | int update_map(st_game *p,int n); 80 | void print_game_map(st_game *p); 81 | void print_name(st_game *p); 82 | void print_land(st_game *p,int ); 83 | void print_p_info(st_game *game,int index); 84 | 85 | //初始化 86 | void init_land(st_land_info (*p_land)[]); 87 | void init_shop(st_map_info *map); 88 | void deinit_game(st_game **p); 89 | st_game * init_game(int player_num); 90 | 91 | 92 | //land结构体相关函数 93 | int get_land_price(st_land_info *land); 94 | int get_land_tlnum(st_land_info *land); 95 | void set_land_player(st_land_info *land,st_player *p_player); 96 | void set_land_color(st_land_info *land,e_color color); 97 | void set_land_price(st_land_info *land,int price); 98 | void set_land_level(st_land_info *land,House_type level); 99 | void set_land_symbol(st_land_info *land,char symbol); 100 | void set_land_ownclor(st_land_info *land,e_color color); 101 | char get_land_symbol(st_land_info *land); 102 | e_color get_land_color(st_land_info *land); 103 | e_color get_land_ownclor(st_land_info *land); 104 | st_player *get_land_player(st_land_info *land); 105 | House_type get_land_level(st_land_info *land); 106 | e_LandOwnerType whose_land(st_game *game,st_land_info *land); 107 | void add_land_tool(st_land_info *land,int id); 108 | 109 | //game结构体相关函数 110 | int get_game_pnum(st_game *p); 111 | int get_game_id(st_game *p); 112 | void set_game_id(st_game *p,int id); 113 | void set_game_pnum(st_game *p,int num); 114 | void next_player(st_game *game); 115 | st_player *get_player(st_game *p); 116 | st_player *get_playern(st_game *p,int id); 117 | st_land_info *get_game_land(st_game *p,int ); 118 | 119 | //cmd相关函数 120 | int buy_land(st_game *game,st_land_info *land); 121 | int upgrade_house(st_game *game,st_land_info *land); 122 | int is_yes(); 123 | int pay_rent(st_game *game,st_land_info *pland); 124 | void player_quit(st_game *game,int id); 125 | void shop(st_game *game); 126 | void use_tool(st_game *game); 127 | int explore_block(st_game *game,int n); 128 | void meet_Block(st_game *game,int x); 129 | void NeedU2realize(st_game *game,int n); 130 | int get_num(); 131 | int is_myturn(st_game *p); 132 | void clear_turnflag(st_game *p); 133 | void set_game_turn(st_game *p,int turn); 134 | 135 | 136 | 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /inc/map.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_H 2 | #define MAP_H 3 | 4 | #define WIDTH 8 5 | #define HEIGHT 5 //map和value也要跟着修改 6 | #define WIDTH_S 7 7 | #define HEIGHT_S 4 //map和value也要跟着修改 8 | typedef struct { 9 | char map[HEIGHT][WIDTH]; //地图信息 10 | int (*value)[WIDTH]; //地块初始价值? 11 | int x,y; //当前游标位置cur 12 | }st_map_info; 13 | 14 | typedef enum House{Level_0=0,Level_1,Level_2,Level_3}House_type; 15 | #define MAX_LEVEL 4 16 | st_map_info * init_map_info(); 17 | void deinit_map_info(st_map_info **p); 18 | void print_map(st_map_info *p); 19 | void get_n_pos(int n,int *x,int *y); 20 | void set_map_cur(st_map_info *p,int x,int y); 21 | void get_map_cru(st_map_info *p,int *x,int *y); 22 | void set_map_e(st_map_info *p,char e,int x,int y); 23 | char get_map_e(st_map_info *p,int x,int y); 24 | int get_map_value(st_map_info *p,int x,int y); 25 | #endif -------------------------------------------------------------------------------- /inc/player.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_H 2 | #define PLAYER_H 3 | #define MAX_PLAYER_NUM 4 4 | #define INIT_MONEY 1000 5 | #define MAX_MONEY 10000 6 | #define MAX_NAME_LEN 15 7 | #include"util.h" 8 | #define MAX_TOOL_NUM 10 9 | typedef struct player{ 10 | char symbol; //玩家在地图上的标志 11 | char *name; //玩家的名字 12 | int id; //玩家id 13 | int x,y; //玩家在地图上的位置pos 14 | int money; //玩家金钱数 15 | int step; //玩家在地图相对起点的步数 16 | e_color color; //玩家颜色 17 | int tool_num; //玩家道具数量 18 | int tool_table[MAX_TOOL_NUM];//存储道具id 19 | }st_player; 20 | 21 | int get_player_step(st_player *p); 22 | int get_player_id(st_player *p); 23 | int get_player_money(st_player *p); 24 | void set_player_step(st_player *p,int step); 25 | void deinit_player(st_player **p); 26 | void get_player_pos(st_player *p,int *x,int *y); 27 | void set_player_pos(st_player *p,int x,int y); 28 | void set_player_money(st_player *p,int money); 29 | void print_player_info(st_player *p); 30 | char get_player_symbol(st_player *p); 31 | char *get_player_name(st_player *p); 32 | st_player *init_player(int id); 33 | e_color get_player_color(st_player *p); 34 | 35 | //道具相关 36 | int get_player_tln(st_player *p,int index); 37 | int get_player_tlnum(st_player *p); 38 | void init_player_tool(st_player *p); 39 | void del_player_tool(st_player *p,int id); 40 | void add_player_tool(st_player *p,int id); 41 | void set_player_tln(st_player *p,int index,int id); 42 | void set_player_tlnum(st_player *p,int num); 43 | 44 | #endif -------------------------------------------------------------------------------- /inc/rich_client.h: -------------------------------------------------------------------------------- 1 | #ifndef RICH_CLIENT_H 2 | #define RICH_CLIENT_H 3 | #include "wrap.h" 4 | 5 | 6 | int init_client(int *); 7 | int is_notice(int fd); 8 | int upload_cmd(int sockfd); 9 | int download_cmd(int sockfd,char *des); 10 | int client_run(int sockfd,char *des_buf); 11 | #define MAXLINE 80 12 | 13 | 14 | void TELL_WAIT(void); /* parent/child from {Sec race_conditions} */ 15 | void TELL_PARENT(pid_t); 16 | void TELL_CHILD(pid_t); 17 | void WAIT_PARENT(void); 18 | void WAIT_CHILD(void); 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /inc/rich_server.h: -------------------------------------------------------------------------------- 1 | #ifndef RICH_SERVER_H 2 | #define RICH_SERVER_H 3 | 4 | int server_run(void); 5 | int init_server(void); 6 | int wait_client(int); 7 | void del_client(int index); 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /inc/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | typedef enum Color{ 4 | None=0,Green=32,Red=31,Blue=34,Yellow=33 5 | }e_color; 6 | //#define AUTO_TEST 7 | #ifndef AUTO_TEST 8 | #define LINUX_CONSOLE 9 | #endif 10 | int window_col(); 11 | void print_color(char c,e_color color); 12 | void print_color_str(char *str,e_color color); 13 | void print_line(char c,int num); 14 | void print_bar(char *str,char c,e_color color); 15 | int six_rand(); 16 | int str2int(const char *str); 17 | int is_digtial(char c); 18 | #endif 19 | 20 | 21 | 22 | /* 23 | #if 0 24 | 我们知道,使用ls命令列出文件列表时,不同的文件类型会用不同的颜色显示。那么如何实现这样带颜色的文本输出呢?答案并不复杂,不管是用shell还是C语言。 25 | 26 | 一、shell下的实现方法 27 | 28 | 先来讲在shell下,如何实现。用echo命令就可以实现,参看以下例子: 29 | 30 | echo -e "\033[32mHello, world!" 31 | 32 | 当你在终端里敲下这条命令后,是不是发现系统用绿色输出了"Hello,world!",不止如此,连之后的命令提示符都变成了绿色?不要着急,听我继续说。echo命令-e选项的作用是激活终端对反斜线转义符(即\)的解释。引号内\033用于引导非常规字符序列,在这里的作用就是引导设置输出属性,后边的[32m就是将前景色设置为绿色,字母m表示设置的属性类别,数字代表属性值。设置可以单独使用,例如: 33 | 34 | echo -e "\033[0m" 35 | 36 | 这行命令的作用是恢复属性为默认值,也就是说0m设置项用于恢复默认值。现在你的终端是不是又一切正常了? 37 | 38 | 理解了这些,剩下的就简单了。用这种命令,除了设置文本前景色,还可以设置很多属性。下边列出其他的设置项: 39 | 40 | -------------------------------------------------------------------------- 41 | 42 | \033[0m 关闭所有属性 43 | \033[1m 设置高亮度 44 | \033[4m 下划线 45 | \033[5m 闪烁 46 | \033[7m 反显 47 | \033[8m 消隐 48 | \033[30m 至 \33[37m 设置前景色 49 | \033[40m 至 \33[47m 设置背景色 50 | \033[nA 光标上移n行 51 | \033[nB 光标下移n行 52 | \033[nC 光标右移n行 53 | \033[nD 光标左移n行 54 | \033[y;xH设置光标位置 55 | \033[2J 清屏 56 | \033[K 清除从光标到行尾的内容 57 | \033[s 保存光标位置 58 | \033[u 恢复光标位置 59 | \033[?25l 隐藏光标 60 | \033[?25h 显示光标 61 | 62 | -------------------------------------------------------------------------- 63 | 64 | 各数字所代表的颜色如下: 65 | 66 | 字背景颜色范围:40----49 67 | 40:黑 68 | 41:深红 69 | 42:绿 70 | 43:黄色 71 | 44:蓝色 72 | 45:紫色 73 | 46:深绿 74 | 47:白色 75 | 76 | 字颜色:30-----------39 77 | 30:黑 78 | 31:红 79 | 32:绿 80 | 33:黄 81 | 34:蓝色 82 | 35:紫色 83 | 36:深绿 84 | 37:白色 85 | 86 | 另外,同类的多种设置项可以组合在一起,中间用分号(;)隔开。如下: 87 | 88 | echo -e "\033[20;1H\033[1;4;32mHello,world\033[0m" 89 | 90 | 这行命令首先\033[20;1H将光标移动到终端第20行第1列,之后的\033[1;4;32m将文本属性设置为高亮、带下划线且颜色为绿色,然后输出Hello,world;最后\033[0m将终端属性恢复为默认值,这样就不会看到连命令完成后的命令提示符也变了样儿了。 91 | 92 | 通过以上各种命令的组合就可以实现对终端输出地复杂控制。 93 | 94 | 二、如何在C编程中实现? 95 | 96 | 理解了以上在Shell中的实现方法,关于在C中如何实现就很简单了。可以说只需要用printf函数代替上边的echo -e就OK了。参见下例: 97 | 98 | int color = 32; 99 | 100 | printf("\033[20;1H\033[1;4;%dmHello, world.\033[0m", color); 101 | 102 | 这个例子类似上边shell中最后那个例子,只是这里颜色值通过变量color来指定(当然,也可以直接指定)。 103 | 104 | 三、联想 105 | 106 | 看到这里你可能会想,是不是在其他编程语言里也可以用类似的方法实现对终端输出的控制呢?答案是肯定的!比如在python中,可以如下输出: 107 | 108 | color=32 109 | 110 | print “\033[20;1H\033[1;4;%dHello, world.\033[0m"%color 111 | 112 | 这个例子的效果跟上边C的例子是相同的。 113 | 114 | #endif 115 | */ -------------------------------------------------------------------------------- /inc/wrap.h: -------------------------------------------------------------------------------- 1 | #ifndef WRAP_H 2 | #define WRAP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void perr_exit(const char *s); 11 | int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); 12 | void Bind(int fd, const struct sockaddr *sa, socklen_t salen); 13 | void Connect(int fd, const struct sockaddr *sa, socklen_t salen); 14 | void Listen(int fd, int backlog); 15 | int Socket(int family, int type, int protocol); 16 | ssize_t Read(int fd, void *ptr, size_t nbytes); 17 | ssize_t Read(int fd, void *ptr, size_t nbytes); 18 | void Close(int fd); 19 | ssize_t Readn(int fd, void *vptr, size_t n); 20 | ssize_t Writen(int fd, const void *vptr, size_t n); 21 | ssize_t Readline(int fd, void *vptr, size_t maxlen); 22 | ssize_t Write(int fd, const void *ptr, size_t nbytes); 23 | 24 | #endif -------------------------------------------------------------------------------- /net/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -I ../inc 3 | VPATH=../inc 4 | all:rich_client.o rich_server.o 5 | rich_client.o:rich_client.h wrap.h error.h 6 | $(CC) $(CFLAGS) -c rich_client.c wrap.c error.c 7 | #cp rich_client.o ../src 8 | rich_server.o:rich_server.h 9 | $(CC) $(CFLAGS) -o rich_server.out rich_server.c wrap.c 10 | cp rich_server.out rich_client.o wrap.o ../src 11 | -------------------------------------------------------------------------------- /net/client.c: -------------------------------------------------------------------------------- 1 | /* client.c */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "wrap.h" 7 | 8 | #define MAXLINE 80 9 | #define SERV_PORT 8000 10 | int clientIndex; 11 | int currentIndex; 12 | int main(int argc, char *argv[]) 13 | { 14 | struct sockaddr_in servaddr; 15 | char buf[MAXLINE]; 16 | int sockfd, n; 17 | 18 | sockfd = Socket(AF_INET, SOCK_STREAM, 0); 19 | 20 | bzero(&servaddr, sizeof(servaddr)); 21 | servaddr.sin_family = AF_INET; 22 | inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); 23 | servaddr.sin_port = htons(SERV_PORT); 24 | 25 | Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 26 | n = Read(sockfd, buf, MAXLINE); 27 | clientIndex=buf[0]-'0'; 28 | printf("my id is %d\n",clientIndex); 29 | while (fgets(buf, MAXLINE, stdin) != NULL) { 30 | Write(sockfd, buf, strlen(buf)); 31 | n = Read(sockfd, buf, MAXLINE); 32 | if (n == 0) 33 | printf("the other side has been closed.\n"); 34 | else 35 | Write(STDOUT_FILENO, buf, n); 36 | } 37 | Close(sockfd); 38 | return 0; 39 | } -------------------------------------------------------------------------------- /net/error.c: -------------------------------------------------------------------------------- 1 | #include /* for definition of errno */ 2 | #include /* ISO C variable aruments */ 3 | #include 4 | #include 5 | #include 6 | static void err_doit(int, int, const char *, va_list); 7 | 8 | /* 9 | * Nonfatal error related to a system call. 10 | * Print a message and return. 11 | */ 12 | void 13 | err_ret(const char *fmt, ...) 14 | { 15 | va_list ap; 16 | 17 | va_start(ap, fmt); 18 | err_doit(1, errno, fmt, ap); 19 | va_end(ap); 20 | } 21 | 22 | 23 | /* 24 | * Fatal error related to a system call. 25 | * Print a message and terminate. 26 | */ 27 | void 28 | err_sys(const char *fmt, ...) 29 | { 30 | va_list ap; 31 | 32 | va_start(ap, fmt); 33 | err_doit(1, errno, fmt, ap); 34 | va_end(ap); 35 | exit(1); 36 | } 37 | 38 | 39 | /* 40 | * Fatal error unrelated to a system call. 41 | * Error code passed as explict parameter. 42 | * Print a message and terminate. 43 | */ 44 | void 45 | err_exit(int error, const char *fmt, ...) 46 | { 47 | va_list ap; 48 | 49 | va_start(ap, fmt); 50 | err_doit(1, error, fmt, ap); 51 | va_end(ap); 52 | exit(1); 53 | } 54 | 55 | 56 | /* 57 | * Fatal error related to a system call. 58 | * Print a message, dump core, and terminate. 59 | */ 60 | void 61 | err_dump(const char *fmt, ...) 62 | { 63 | va_list ap; 64 | 65 | va_start(ap, fmt); 66 | err_doit(1, errno, fmt, ap); 67 | va_end(ap); 68 | abort(); /* dump core and terminate */ 69 | exit(1); /* shouldn't get here */ 70 | } 71 | 72 | 73 | /* 74 | * Nonfatal error unrelated to a system call. 75 | * Print a message and return. 76 | */ 77 | void 78 | err_msg(const char *fmt, ...) 79 | { 80 | va_list ap; 81 | 82 | va_start(ap, fmt); 83 | err_doit(0, 0, fmt, ap); 84 | va_end(ap); 85 | } 86 | 87 | 88 | /* 89 | * Fatal error unrelated to a system call. 90 | * Print a message and terminate. 91 | */ 92 | void 93 | err_quit(const char *fmt, ...) 94 | { 95 | va_list ap; 96 | 97 | va_start(ap, fmt); 98 | err_doit(0, 0, fmt, ap); 99 | va_end(ap); 100 | exit(1); 101 | } 102 | 103 | 104 | /* 105 | * Print a message and return to caller. 106 | * Caller specifies "errnoflag". 107 | */ 108 | #define MAXLINE 80 109 | static void 110 | err_doit(int errnoflag, int error, const char *fmt, va_list ap) 111 | { 112 | char buf[MAXLINE]; 113 | vsnprintf(buf, MAXLINE, fmt, ap); 114 | if (errnoflag) 115 | snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s", 116 | strerror(error)); 117 | strcat(buf, "\n"); 118 | fflush(stdout); /* in case stdout and stderr are the same */ 119 | fputs(buf, stderr); 120 | fflush(NULL); /* flushes all stdio output streams */ 121 | } 122 | 123 | -------------------------------------------------------------------------------- /net/rich_client.c: -------------------------------------------------------------------------------- 1 | /* client.c */ 2 | /* 3 | * v0.1 客户端首先对服务器发送的数据进行判断,是否为通知上传cmd,若是则获取输入并上传, 4 | * 否则等待服务器下发的指令cmd。 5 | * v0.2 修改download_cmd();在命令结尾添加'\0',使用外部空间存储,修改client_run的返回值 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | //inet_ntop 15 | #include 16 | #include 17 | #include 18 | 19 | #include "rich_client.h" 20 | 21 | #define SERV_PORT 8000 22 | int clientIndex; 23 | int currentIndex; 24 | 25 | 26 | 27 | 28 | 29 | #if 0 30 | int main(int argc, char *argv[]) 31 | { 32 | client_run(init_client()); 33 | } 34 | #endif 35 | 36 | int client_run(int sockfd,char *des_buf) 37 | { 38 | 39 | if(-1==sockfd) return -1; 40 | int IsYourturn=0; 41 | if(is_notice(sockfd)) //服务器通知 42 | { 43 | upload_cmd(sockfd); 44 | IsYourturn=1; 45 | } 46 | download_cmd(sockfd,des_buf); 47 | 48 | return IsYourturn; 49 | 50 | } 51 | 52 | 53 | int init_client(int *PlayerNum) 54 | { 55 | struct sockaddr_in servaddr; 56 | int sockfd; 57 | 58 | sockfd = Socket(AF_INET, SOCK_STREAM, 0); 59 | 60 | bzero(&servaddr, sizeof(servaddr)); 61 | servaddr.sin_family = AF_INET; 62 | inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); 63 | servaddr.sin_port = htons(SERV_PORT); 64 | 65 | Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 66 | 67 | char data_rev[2]; 68 | /*之前在这里用全局的recv_buf[],导致之后用recv_buf[]第一次接收的数据总是少一个*/ 69 | /*后来用char *data_rev来接收单个字节,但是指针没有初始化,导致段错误*/ 70 | 71 | int n = Read(sockfd, data_rev, 2);/*接收服务器发来的client[]的索引Index*/ 72 | 73 | if(n<=0) 74 | { 75 | printf("init client fail\n"); 76 | return -1; 77 | } 78 | clientIndex=data_rev[0]; 79 | printf("my id is %d\n",clientIndex); 80 | *PlayerNum=data_rev[1]; 81 | 82 | return sockfd; 83 | } 84 | 85 | /*如果发送的索引和自己的索引一致,则表示服务器在通知自己*/ 86 | int is_notice(int sockfd) 87 | { 88 | int n; 89 | char data_rev; 90 | n=Readn(sockfd,&data_rev, 1); 91 | if(n <= 0) 92 | { 93 | printf("wait server notice error:the other side has been closed or sth wrong.\n"); 94 | Close(sockfd); 95 | exit(-1); 96 | } 97 | return data_rev == clientIndex; 98 | } 99 | 100 | /*获取输入 上传指令数据*/ 101 | int upload_cmd(int sockfd) 102 | { 103 | char send_buf[MAXLINE]; 104 | 105 | if(fgets(send_buf, MAXLINE, stdin) == NULL) exit(-1); 106 | Writen(sockfd, send_buf, strlen(send_buf)); 107 | return 0; 108 | } 109 | 110 | /*下载数据指令*/ 111 | int download_cmd(int sockfd,char *des) 112 | { 113 | int n; 114 | char recv_buf[MAXLINE]; 115 | n = Read(sockfd, recv_buf, MAXLINE); 116 | if(n <= 0) 117 | { 118 | printf("download cmd error:the other side has been closed or sth wrong.\n"); 119 | Close(sockfd); 120 | exit(-1); 121 | } 122 | memcpy(des,recv_buf,sizeof(recv_buf)); 123 | n=0; 124 | while(des[n]!='\n'&& n++ < MAXLINE-1);//暂时这么写 125 | des[n]='\0'; 126 | return 0; 127 | } 128 | 129 | #if 1 130 | /* TELL_*函数的signal实现 */ 131 | #include 132 | #include "error.h" 133 | static volatile sig_atomic_t sigflag; 134 | static sigset_t newmask, oldmask, zeromask; 135 | 136 | static void 137 | sig_usr(int signo) 138 | { 139 | sigflag = 1; 140 | } 141 | 142 | void 143 | TELL_WAIT(void) 144 | { 145 | if(signal(SIGUSR1, sig_usr) == SIG_ERR) 146 | err_sys("signal(SIGUSR1) error"); 147 | if(signal(SIGUSR2, sig_usr) == SIG_ERR) 148 | err_sys("signal(SIGUSR2) error"); 149 | sigemptyset(&zeromask); 150 | sigemptyset(&newmask); 151 | sigaddset(&newmask, SIGUSR1); 152 | sigaddset(&newmask, SIGUSR2); 153 | 154 | if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) 155 | err_sys("SIG_BLOCK error"); 156 | } 157 | 158 | void 159 | TELL_PARENT(pid_t pid) 160 | { 161 | kill(pid, SIGUSR2); 162 | } 163 | 164 | void 165 | WAIT_PARENT(void) 166 | { 167 | while(sigflag == 0) 168 | sigsuspend(&zeromask); 169 | sigflag = 0; 170 | 171 | if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) 172 | err_sys("SIG_SETMASK error"); 173 | } 174 | 175 | void 176 | TELL_CHILD(pid_t pid) 177 | { 178 | kill(pid, SIGUSR1); 179 | } 180 | 181 | void 182 | WAIT_CHILD(void) 183 | { 184 | while(sigflag == 0) 185 | sigsuspend(&zeromask); 186 | sigflag = 0; 187 | if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) 188 | err_sys("SIG_SETMASK error"); 189 | } 190 | 191 | #endif 192 | 193 | -------------------------------------------------------------------------------- /net/rich_server.c: -------------------------------------------------------------------------------- 1 | /* server.c */ 2 | /* 3 | * v0.1 服务器和客户端程序,服务器端通知指定客户端上传指令,接收后把cmd广播给每个客户端 4 | * 当接收到字符'x'后切换客户端索引号 5 | */ 6 | 7 | /*说明 8 | step server client 9 | 1 write to N ---> read 服务器通知指定客户N响应 所有客户端read, 10 | 2 read from N <--- N write 客户端N响应,将数据上传,其他客户等待下载 11 | 3 write to All ---> read 所有客户端下载数据 12 | ... ... 13 | 14 | 重复1 2 3,如果N上传的数据首为'x',代表结束,服务器切换通知N+1响应 15 | 如果step2中客户N断开连接,则使用del_client删除该sockfd 16 | */ 17 | 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "wrap.h" 25 | 26 | //toupper 27 | #include 28 | 29 | //inet_ntop 30 | #include 31 | #include 32 | #include 33 | 34 | #include "rich_server.h" 35 | #include 36 | #include "rich_client.h" 37 | 38 | #define MAXLINE 80 39 | #define SERV_PORT 8000 40 | 41 | int maxi, maxfd, listenfd; 42 | int client[FD_SETSIZE]; 43 | fd_set allset; 44 | int g_currentIndex; 45 | int g_index; 46 | int player_num; 47 | 48 | #if 1 49 | int main(int argc, char **argv) 50 | { 51 | g_currentIndex=0; 52 | init_server(); 53 | TELL_PARENT(getppid()); 54 | wait_client(*argv[1]);//*argv[1]代表人数 55 | printf("You can begin the game!\n"); 56 | server_run(); 57 | return 0; 58 | } 59 | #endif 60 | 61 | 62 | /*捕获write不成功时产生的sigpipe信号 63 | *write不成功是由于对方链接关闭引起的 64 | */ 65 | static void 66 | write_err(int signo) 67 | { 68 | printf("a player quit\n"); 69 | client[g_index]=-1; 70 | player_num--; 71 | return; 72 | } 73 | 74 | 75 | int init_server(void) 76 | { 77 | struct sockaddr_in servaddr; 78 | listenfd = Socket(AF_INET, SOCK_STREAM, 0); 79 | 80 | bzero(&servaddr, sizeof(servaddr)); 81 | servaddr.sin_family = AF_INET; 82 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 83 | servaddr.sin_port = htons(SERV_PORT); 84 | 85 | 86 | Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 87 | 88 | Listen(listenfd, 20); 89 | 90 | maxfd = listenfd; /* initialize */ 91 | maxi = -1; /* index into client[] array */ 92 | int i; 93 | for (i = 0; i < FD_SETSIZE; i++) 94 | client[i] = -1; /* -1 indicates available entry */ 95 | FD_ZERO(&allset); 96 | FD_SET(listenfd, &allset); 97 | 98 | if(signal(SIGPIPE, write_err) == SIG_ERR) 99 | { 100 | printf("server signal action init error\n"); 101 | exit(0); 102 | } 103 | return 0; 104 | 105 | } 106 | 107 | int wait_client(int num) 108 | { 109 | int client_num,i,nready,connfd; 110 | fd_set rset; 111 | socklen_t cliaddr_len; 112 | struct sockaddr_in cliaddr; 113 | char str[INET_ADDRSTRLEN]; 114 | for(client_num=0;client_num < num;)//等待其他2个客户端连接 115 | { 116 | rset = allset; /* structure assignment */ 117 | nready = select(maxfd+1, &rset, NULL, NULL, NULL); 118 | if (nready < 0) 119 | perr_exit("select error"); 120 | 121 | if (FD_ISSET(listenfd, &rset)) { /* new client connection */ 122 | cliaddr_len = sizeof(cliaddr); 123 | connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); 124 | printf("A player join the game:\n"); 125 | printf("received from %s at PORT %d\n", 126 | inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 127 | ntohs(cliaddr.sin_port)); 128 | 129 | for (i = 0; i < FD_SETSIZE; i++) 130 | if (client[i] < 0) { 131 | client[i] = connfd; /* save descriptor */ 132 | break; 133 | } 134 | 135 | if (i == FD_SETSIZE) { 136 | fputs("too many clients\n", stderr); 137 | exit(1); 138 | } 139 | char send[2]; 140 | send[0]=i; //client id 141 | send[1]=num; //player num 142 | Write(connfd,send,2); 143 | FD_SET(connfd, &allset); /* add new descriptor to set */ 144 | if (connfd > maxfd) 145 | maxfd = connfd; /* for select */ 146 | if (i > maxi) 147 | maxi = i; /* max index in client[] array */ 148 | 149 | client_num++; 150 | 151 | } 152 | 153 | } 154 | return 0; 155 | 156 | } 157 | 158 | 159 | /* 160 | * 客户端和服务器建立连接后,所有客户端等待服务器通知 161 | * 服务器只向当前被激活的客户端接收数据 162 | */ 163 | int server_run(void) 164 | { 165 | ssize_t n; 166 | char send_buf[MAXLINE]; 167 | char recv_buf[MAXLINE]; 168 | int sockfd; 169 | player_num=maxi+1;/*maxi=-1 表示无连接*/ 170 | g_currentIndex=0; 171 | while(player_num > 0) 172 | { 173 | 174 | //printf("current index=%d\n",g_currentIndex); 175 | sockfd = client[g_currentIndex];//只对当前id的连接处理 176 | 177 | if(sockfd == -1) 178 | { 179 | if(++g_currentIndex > maxi) 180 | g_currentIndex=0; 181 | continue; 182 | //return -1; //something wrong 183 | } 184 | 185 | send_buf[0]=g_currentIndex; 186 | 187 | for(g_index=0; g_index <= maxi; g_index++) //通知指定客户端发送数据 188 | if(client[g_index]!=-1) 189 | Writen(client[g_index], send_buf, 1); 190 | 191 | if ( (n = Read(sockfd, recv_buf, MAXLINE)) == 0) { 192 | /* connection closed by client */ 193 | Close(sockfd); 194 | FD_CLR(sockfd, &allset); 195 | client[g_currentIndex]=-1; 196 | player_num--; 197 | //del_client(g_currentIndex); 198 | } 199 | else 200 | { 201 | ssize_t j; 202 | for (j = 0; j < n; j++) 203 | send_buf[j] =recv_buf[j]; 204 | for(g_index=0; g_index <= maxi; g_index++) 205 | { 206 | if(client[g_index]!=-1) 207 | Writen(client[g_index], send_buf, n); 208 | usleep(1000); //测试中只有一块网卡 不加延时会丢失数据 209 | } 210 | 211 | if(recv_buf[0]!='x') continue;//如果客户端不发送x,则继续 212 | } 213 | 214 | if(++g_currentIndex > maxi) g_currentIndex=0; //下一个客户 215 | 216 | } 217 | return 0; 218 | } 219 | 220 | #if 0 221 | int notice_client(int fd) 222 | { 223 | char c; 224 | Write(fd,&c, 1); 225 | } 226 | 227 | int recv_client(int fd) 228 | { 229 | ssize_t n = Read(sockfd, buf, MAXLINE); 230 | if(n==0) 231 | { 232 | /* connection closed by client */ 233 | Close(sockfd); 234 | FD_CLR(sockfd, &allset); 235 | del_client(g_currentIndex); 236 | 237 | } 238 | 239 | } 240 | #endif 241 | /*当client断开连接后删除该客户*/ 242 | void del_client(int index) 243 | { 244 | int i; 245 | for(i = index; i < maxi; i++) 246 | client[i]=client[i+1]; 247 | client[maxi]=-1; 248 | maxi--; 249 | } 250 | 251 | -------------------------------------------------------------------------------- /net/server.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaodianbo/richman/408e81d7af42c4bf84037413afb7613c3d585e54/net/server.c -------------------------------------------------------------------------------- /net/server_select.c: -------------------------------------------------------------------------------- 1 | /* server.c */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "wrap.h" 7 | 8 | #define MAXLINE 80 9 | #define SERV_PORT 8000 10 | 11 | int main(int argc, char **argv) 12 | { 13 | int i, maxi, maxfd, listenfd, connfd, sockfd; 14 | int nready, client[FD_SETSIZE]; 15 | ssize_t n; 16 | fd_set rset, allset; 17 | char buf[MAXLINE]; 18 | char str[INET_ADDRSTRLEN]; 19 | socklen_t cliaddr_len; 20 | struct sockaddr_in cliaddr, servaddr; 21 | 22 | listenfd = Socket(AF_INET, SOCK_STREAM, 0); 23 | 24 | bzero(&servaddr, sizeof(servaddr)); 25 | servaddr.sin_family = AF_INET; 26 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 27 | servaddr.sin_port = htons(SERV_PORT); 28 | 29 | Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 30 | 31 | Listen(listenfd, 20); 32 | 33 | maxfd = listenfd; /* initialize */ 34 | maxi = -1; /* index into client[] array */ 35 | for (i = 0; i < FD_SETSIZE; i++) 36 | client[i] = -1; /* -1 indicates available entry */ 37 | FD_ZERO(&allset); 38 | FD_SET(listenfd, &allset); 39 | 40 | for ( ; ; ) { 41 | rset = allset; /* structure assignment */ 42 | nready = select(maxfd+1, &rset, NULL, NULL, NULL); 43 | if (nready < 0) 44 | perr_exit("select error"); 45 | 46 | if (FD_ISSET(listenfd, &rset)) { /* new client connection */ 47 | cliaddr_len = sizeof(cliaddr); 48 | connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); 49 | 50 | printf("received from %s at PORT %d\n", 51 | inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 52 | ntohs(cliaddr.sin_port)); 53 | 54 | for (i = 0; i < FD_SETSIZE; i++) 55 | if (client[i] < 0) { 56 | client[i] = connfd; /* save descriptor */ 57 | break; 58 | } 59 | if (i == FD_SETSIZE) { 60 | fputs("too many clients\n", stderr); 61 | exit(1); 62 | } 63 | 64 | FD_SET(connfd, &allset); /* add new descriptor to set */ 65 | if (connfd > maxfd) 66 | maxfd = connfd; /* for select */ 67 | if (i > maxi) 68 | maxi = i; /* max index in client[] array */ 69 | 70 | if (--nready == 0) 71 | continue; /* no more readable descriptors */ 72 | } 73 | 74 | for (i = 0; i <= maxi; i++) { /* check all clients for data */ 75 | if ( (sockfd = client[i]) < 0) 76 | continue; 77 | if (FD_ISSET(sockfd, &rset)) { 78 | if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { 79 | /* connection closed by client */ 80 | Close(sockfd); 81 | FD_CLR(sockfd, &allset); 82 | client[i] = -1; 83 | } else { 84 | int j; 85 | for (j = 0; j < n; j++) 86 | buf[j] = toupper(buf[j]); 87 | Write(sockfd, buf, n); 88 | } 89 | 90 | if (--nready == 0) 91 | break; /* no more readable descriptors */ 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /net/wrap.c: -------------------------------------------------------------------------------- 1 | #include"wrap.h" 2 | 3 | 4 | void perr_exit(const char *s) 5 | { 6 | perror(s); 7 | exit(1); 8 | } 9 | 10 | int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) 11 | { 12 | int n; 13 | 14 | again: 15 | if ( (n = accept(fd, sa, salenptr)) < 0) { 16 | if ((errno == ECONNABORTED) || (errno == EINTR)) 17 | goto again; 18 | else 19 | perr_exit("accept error"); 20 | } 21 | return n; 22 | } 23 | 24 | void Bind(int fd, const struct sockaddr *sa, socklen_t salen) 25 | { 26 | if (bind(fd, sa, salen) < 0) 27 | perr_exit("bind error"); 28 | } 29 | 30 | void Connect(int fd, const struct sockaddr *sa, socklen_t salen) 31 | { 32 | if (connect(fd, sa, salen) < 0) 33 | perr_exit("connect error"); 34 | } 35 | 36 | void Listen(int fd, int backlog) 37 | { 38 | if (listen(fd, backlog) < 0) 39 | perr_exit("listen error"); 40 | } 41 | 42 | int Socket(int family, int type, int protocol) 43 | { 44 | int n; 45 | 46 | if ( (n = socket(family, type, protocol)) < 0) 47 | perr_exit("socket error"); 48 | return n; 49 | } 50 | 51 | ssize_t Read(int fd, void *ptr, size_t nbytes) 52 | { 53 | ssize_t n; 54 | 55 | again: 56 | if ( (n = read(fd, ptr, nbytes)) == -1) { 57 | if (errno == EINTR) 58 | goto again; 59 | else 60 | return -1; 61 | } 62 | 63 | 64 | return n; 65 | } 66 | 67 | ssize_t Write(int fd, const void *ptr, size_t nbytes) 68 | { 69 | ssize_t n; 70 | 71 | again: 72 | if ( (n = write(fd, ptr, nbytes)) == -1) { 73 | if (errno == EINTR) 74 | goto again; 75 | else 76 | return -1; 77 | } 78 | return n; 79 | } 80 | 81 | void Close(int fd) 82 | { 83 | if (close(fd) == -1) 84 | perr_exit("close error"); 85 | } 86 | 87 | 88 | ssize_t Readn(int fd, void *vptr, size_t n) 89 | { 90 | size_t nleft; 91 | ssize_t nread; 92 | char *ptr; 93 | 94 | ptr = vptr; 95 | nleft = n; 96 | while (nleft > 0) { 97 | if ( (nread = read(fd, ptr, nleft)) < 0) { 98 | if (errno == EINTR) 99 | nread = 0; 100 | else 101 | return -1; 102 | } else if (nread == 0) 103 | break; 104 | 105 | nleft -= nread; 106 | ptr += nread; 107 | } 108 | return n - nleft; 109 | } 110 | 111 | ssize_t Writen(int fd, const void *vptr, size_t n) 112 | { 113 | size_t nleft; 114 | ssize_t nwritten; 115 | const char *ptr; 116 | 117 | ptr = vptr; 118 | nleft = n; 119 | while (nleft > 0) { 120 | if ( (nwritten = write(fd, ptr, nleft)) <= 0) { 121 | if (nwritten < 0 && errno == EINTR) 122 | nwritten = 0; 123 | else 124 | return -1; 125 | } 126 | 127 | nleft -= nwritten; 128 | ptr += nwritten; 129 | } 130 | return n; 131 | } 132 | 133 | static ssize_t my_read(int fd, char *ptr) 134 | { 135 | static int read_cnt; 136 | static char *read_ptr; 137 | static char read_buf[100]; 138 | 139 | if (read_cnt <= 0) { 140 | again: 141 | if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { 142 | if (errno == EINTR) 143 | goto again; 144 | return -1; 145 | } else if (read_cnt == 0) 146 | return 0; 147 | read_ptr = read_buf; 148 | } 149 | read_cnt--; 150 | *ptr = *read_ptr++; 151 | return 1; 152 | } 153 | 154 | ssize_t Readline(int fd, void *vptr, size_t maxlen) 155 | { 156 | ssize_t n, rc; 157 | char c, *ptr; 158 | 159 | ptr = vptr; 160 | for (n = 1; n < maxlen; n++) { 161 | if ( (rc = my_read(fd, &c)) == 1) { 162 | *ptr++ = c; 163 | if (c == '\n') 164 | break; 165 | } else if (rc == 0) { 166 | *ptr = 0; 167 | return n - 1; 168 | } else 169 | return -1; 170 | } 171 | *ptr = 0; 172 | return n; 173 | } -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | #在src文件夹和net文件夹下是源程序 inc下是头文件 2 | 3 | #Compile=gcc 4 | #Flag=-Wall 5 | CC=gcc 6 | CFLAGS=-Wall -I ../inc -I ../net 7 | VPATH=../inc ../net 8 | obj=game.o map.o player.o util.o console.o rich.o rich_client.o wrap.o error.o 9 | tar=rich 10 | target:$(obj) 11 | $(CC) -o $(tar) $(obj) $(CFLAGS) 12 | $(CC) $(CFLAGS) -o rich_server.out ../net/rich_server.c ../net/wrap.c ../net/rich_client.c ../net/error.c 13 | rm $(obj) 14 | game.o:game.h player.h map.h util.h rich_client.h 15 | map.o:map.h 16 | player.o:player.h 17 | util.o:util.h 18 | console.o:console.h game.h 19 | wrap.o:wrap.h 20 | error.o:error.h 21 | rich_client.o:rich_client.h wrap.h error.h 22 | rich.o:console.h rich_client.h 23 | clean: 24 | rm rich *.o 25 | -------------------------------------------------------------------------------- /src/console.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "console.h" 3 | #include "rich_client.h" 4 | char cmd_line[LINE_MAX]; 5 | char cmd[LINE_MAX]; 6 | void get_cmd_line(void); 7 | void get_cmd(); 8 | int get_num_para(); 9 | int cmd_go(int n, char *c[]); 10 | int cmd_help(int n, char *c[]); 11 | int cmd_exitgame(int n, char *c[]); 12 | int cmd_player_info(int n,char *c[]); 13 | int cmd_player_quit(int n,char * c[]); 14 | int cmd_roll(int n,char * c[]); 15 | cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len); 16 | int cmd_use_tool(int n, char *c[]); 17 | 18 | st_game *g_game; 19 | int sockfd=-1; 20 | 21 | //所有的cmd函数在这里注册 22 | cmd_tbl_t cmd_tb[CMD_MAX]={{"go",cmd_go}, 23 | {"help",cmd_help}, 24 | {"exitgame",cmd_exitgame}, 25 | {"quitgame",cmd_player_quit}, 26 | {"info",cmd_player_info}, 27 | {"roll",cmd_roll}, 28 | {"tool",cmd_use_tool}, 29 | }; 30 | //没有用到 31 | #if 0 32 | land_ftbl_t land_ftb[MAX_LAND_FUN]={{Na,buy_land}, 33 | {Owner,buy_land}, 34 | {Other,pay_rent}, 35 | {Toolshop,NULL}, 36 | } 37 | #endif 38 | 39 | int cmd_use_tool(int n, char *c[]) 40 | { 41 | use_tool(g_game); 42 | return 0; 43 | } 44 | 45 | int cmd_roll(int n,char * c [ ]) 46 | { 47 | cmd_go(six_rand(),NULL); 48 | return 0; 49 | } 50 | int cmd_go(int n, char *c[]) 51 | { 52 | n=explore_block(g_game,n); 53 | printf("%s go %d steps.\n",get_p_name(g_game),n); 54 | 55 | update_map(g_game,n); //更新地图 改变玩家位置信息 56 | print_game_map(g_game); 57 | int step=get_p_step(g_game); //获取玩家land上的位置 58 | st_land_info *land=get_game_land(g_game,step); //得到第pos个地块 59 | e_LandOwnerType ownertype=whose_land(g_game,land); 60 | 61 | switch(ownertype) 62 | { 63 | case Na: 64 | buy_land(g_game,land); 65 | break; 66 | case Owner: 67 | upgrade_house(g_game,land); 68 | break; 69 | case Other: 70 | pay_rent(g_game,land); 71 | break; 72 | case Toolshop: 73 | shop(g_game); 74 | break; 75 | default: 76 | printf("something wrong with LandOwnerType\n"); 77 | } 78 | 79 | next_player(g_game); 80 | return 0; 81 | } 82 | int cmd_help(int n, char *c[]) 83 | { 84 | printf("cmd summary(support shortcuts)\n"); 85 | printf("roll: go random 1-6 steps.\n"); 86 | printf("go N: go N steps.\n"); 87 | printf("tool: display and use tool\n"); 88 | printf("info: query player N's information.\n"); 89 | printf("quitgame: player quit game.\n"); 90 | printf("exitgame: end the game.\n"); 91 | return 0; 92 | } 93 | 94 | int cmd_exitgame(int n, char *c[]) 95 | { 96 | //做好回收工作 97 | deinit_game(&g_game); 98 | 99 | exit(0); 100 | } 101 | 102 | int cmd_player_quit(int n,char * c [ ]) 103 | { 104 | player_quit(g_game,get_p_id(g_game)); 105 | char buf[MAXLINE]; 106 | 107 | if(sockfd!=-1 ) 108 | { 109 | if(is_myturn(g_game)) 110 | { 111 | next_player(g_game); 112 | exit(0); 113 | } 114 | is_notice(sockfd); 115 | download_cmd(sockfd,buf); 116 | } 117 | return 0; 118 | } 119 | 120 | int cmd_player_info(int n,char *c[]) 121 | { 122 | int i,tmp; 123 | int player_tb[MAX_PLAYER_NUM]; 124 | st_player *player; 125 | int player_num=get_game_pnum(g_game); 126 | printf("please enter the id that you want to query\n"); 127 | //打印玩家名和id号 128 | for(i=0 ; i < player_num; i++) 129 | { 130 | player=get_playern(g_game,i); 131 | player_tb[i]=get_player_id(player); 132 | print_color_str(get_player_name(player),get_player_color(player)); 133 | printf(":id=%d ",player_tb[i]); 134 | } 135 | printf("\nall:9\n"); 136 | tmp=get_num(); 137 | if(tmp==9) 138 | print_p_info(g_game,9); 139 | else 140 | for(i=0;iname!=NULL && strncmp (cmd, cmdtp->name, len) == 0) 217 | { 218 | if (len == strlen (cmdtp->name)) 219 | return cmdtp; /* full match */ 220 | cmdtp_temp = cmdtp; /* abbreviated command ? */ 221 | n_found++; 222 | } 223 | } 224 | if (n_found == 1) 225 | { /* exactly one match */ 226 | return cmdtp_temp; 227 | } 228 | 229 | if(cmd[0]!='\0') 230 | printf("cmd: '%s' not found, try cmd \"help\"?\n",cmd); 231 | 232 | return NULL; /* not found or ambiguous command */ 233 | } 234 | 235 | void playing() 236 | { 237 | start: 238 | update_map(g_game,0); 239 | print_game_map(g_game); 240 | print_p_info(g_game,get_game_id(g_game)); 241 | print_name(g_game); 242 | print_color('>',get_p_color(g_game)); 243 | get_cmd_line(); 244 | get_cmd(); 245 | cmd_tbl_t *do_cmd=find_cmd_tbl(cmd,cmd_tb,CMD_MAX); 246 | if(do_cmd==NULL) goto start; 247 | do_cmd->cmd(get_num_para(),NULL); 248 | 249 | } 250 | 251 | int ParseCmdLine( int argc , char *argv[] ) 252 | { 253 | int i; 254 | for( i=0;i4||PlayerNum<2) PlayerNum=PLAYER_NUM; 296 | } 297 | #endif 298 | int getServPlayNum=PlayerNum; 299 | 300 | if(net_flag==LOCAL) goto initgame; 301 | 302 | //apue中有TELL_WAIT等函数解决进程间同步问题 303 | if(net_flag==NET_SERVER) 304 | { 305 | pid_t pid; 306 | TELL_WAIT(); 307 | if((pid=fork()) < 0 ) 308 | cmd_exitgame(0,NULL); 309 | else if(pid==0)//child 310 | { 311 | 312 | //TELL_PARENT(getppid()); 313 | execl("rich_server.out","rich_server.out",&PlayerNum,NULL); 314 | printf("server fork error\n"); 315 | exit(-1); 316 | } 317 | else 318 | { 319 | WAIT_CHILD(); 320 | } 321 | 322 | } 323 | 324 | sockfd=init_client(&getServPlayNum); 325 | if(sockfd<0) 326 | { 327 | //kill(pid,SIGKILL); 328 | return -1; 329 | } 330 | 331 | initgame: 332 | if((g_game=init_game(getServPlayNum))==NULL) 333 | cmd_exitgame(0,NULL); 334 | 335 | 336 | return 0; 337 | 338 | } 339 | 340 | 341 | #if 0 342 | int main(void) 343 | { 344 | #define PLAYER_NUM 4 345 | g_game=init_game(PLAYER_NUM); 346 | if(g_game==NULL) return 1; 347 | //(p->p_land[LAND_NUM-5]).color=Red; 348 | //set_land_color(&(g_game->p_land[LAND_NUM-5]),Red); 349 | print_game_map(g_game); 350 | update_map(g_game,0); 351 | print_game_map(g_game); 352 | 353 | while(1) 354 | { 355 | print_name(g_game); 356 | print_color('>',get_p_color(g_game)); 357 | get_cmd_line(); 358 | get_cmd(); 359 | cmd_tbl_t *do_cmd=find_cmd_tbl(cmd,cmd_tb,CMD_MAX); 360 | 361 | if(do_cmd==NULL) 362 | { 363 | printf("cmd: '%s' not found, try cmd \"help\"?\n",cmd); 364 | continue; 365 | } 366 | do_cmd->cmd(get_num_para(),NULL); 367 | update_map(g_game,0); 368 | print_game_map(g_game); 369 | //printf("%s\n",cmd_line); 370 | //printf("%s\n",cmd); 371 | } 372 | } 373 | 374 | #endif 375 | -------------------------------------------------------------------------------- /src/game.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include"game.h" 3 | #include 4 | #include 5 | #include 6 | #include "rich_client.h" 7 | extern int sockfd; 8 | 9 | 10 | void put_Block(st_game *,int); 11 | void meet_Block(st_game *,int); 12 | 13 | st_tool tool_tb[MAX_TOOL_NUM]={{'G',"Godwealth",0,500,Green,NeedU2realize,NeedU2realize}, //财神 14 | {'B',"Block",1,200,Red,put_Block,meet_Block}, //路障 15 | {'C',"Control dice",2,200,Green,NeedU2realize,NeedU2realize},//遥控骰子 16 | {'\0',"",0,0,0,NULL,NULL}, 17 | }; 18 | 19 | st_land_info init_land_st={ 20 | x:0, 21 | y:0, 22 | price:0, 23 | p_player:NULL, 24 | level:Level_0, 25 | color:None, 26 | owner_color:None, 27 | tool_n:0, 28 | symbol:0, 29 | }; 30 | 31 | //初始化游戏结构体 包括玩家,地图及地块信息 32 | st_game * init_game(int player_num) 33 | { 34 | st_game *p=malloc(sizeof(st_game)); 35 | if(p==NULL) return NULL; 36 | set_game_pnum(p,player_num); 37 | set_game_id(p,0); 38 | 39 | int i; 40 | for(i=0;ip_player_tab)[i]=init_player(i))==NULL) goto init_player_fail; 42 | 43 | if((p->p_map=init_map_info())==NULL) goto init_map_fail; 44 | init_shop(p->p_map); 45 | init_land(&(p->p_land)); 46 | clear_turnflag(p); 47 | return p; 48 | 49 | init_map_fail: 50 | init_player_fail: 51 | for(;i>=0;i--) 52 | deinit_player(&(p->p_player_tab)[i]); 53 | 54 | free(p); 55 | return NULL; 56 | } 57 | 58 | //结束游戏时销毁游戏结构体 释放相关空间 59 | void deinit_game(st_game **p) 60 | { 61 | if(*p==NULL) return; 62 | int i=get_game_pnum(*p); 63 | for(;i>0;i--) 64 | deinit_player(&((*p)->p_player_tab)[i-1]); 65 | deinit_map_info(&((*p)->p_map)); 66 | free(*p); 67 | *p=NULL; 68 | 69 | } 70 | 71 | 72 | 73 | //打印地图 74 | /*有待优化 75 | *aaaaaaa* 76 | *a a* 77 | *a a* 78 | *aaaaaaa* 79 | */ 80 | void print_map_line(int num,int flag) 81 | { 82 | #ifdef LINUX_CONSOLE 83 | print_line(' ',num); 84 | #else 85 | if(flag==1) 86 | printf("\n"); 87 | #endif 88 | 89 | } 90 | 91 | 92 | /*首先判断land中的symbol是否有符号如果有,打印land中的临时symbol,否则打印地图元素: 93 | 地图是形状 94 | aaaa 95 | b d 96 | cccc 97 | 首先打印a行,然后打印第二行到第height-1行(从b列打到d列),最后打印c行,每行前后加上 98 | 空格,让地图在中间显示。land是一维数组,map是二维数组,可看成将land围绕在map的四周 99 | ,他们的索引有一定的对应关系,也是分成四部分。 100 | */ 101 | void print_game_map(st_game *p) 102 | { 103 | int i,j,k; 104 | st_land_info *pland=get_game_land(p,0); 105 | st_map_info *pmap=p->p_map; 106 | if(p==NULL) return; 107 | print_map_line(window_col()/2-WIDTH/2,0); 108 | for(i=j=0;j0) 124 | printf(" "); 125 | if(get_land_symbol(get_game_land(p,WIDTH+i-1))!=0) 126 | print_land(p,WIDTH+i-1); 127 | else 128 | print_color(p->p_map->map[i][WIDTH-1],(p->p_land[WIDTH+i-1]).owner_color);//打印最后一列 129 | print_map_line(window_col()/2-WIDTH+WIDTH/2,1); 130 | } 131 | 132 | print_map_line(window_col()/2-WIDTH/2,0); 133 | for(i=HEIGHT-1,j=0;jp_map->map[i][j],(p->p_land[LAND_NUM-i-j]).owner_color);//打印最后一行a 138 | print_map_line(window_col()/2-WIDTH+WIDTH/2,1); 139 | 140 | } 141 | 142 | 143 | 144 | //显示地图上的玩家 当玩家重叠时,显示当前玩家 145 | //切换玩家后使用update_map(game,0),显示当前玩家 146 | int update_map(st_game *p,int n) 147 | { 148 | int i=0,tmp; 149 | int player_num=get_game_pnum(p); 150 | st_land_info *land; 151 | //更新除当前玩家外各玩家的地图显示 因为玩家移动后可能清除其它玩家符号 152 | for(;i=0) ? (n+step)%LAND_NUM : (LAND_NUM+(n+step)%LAND_NUM); //负数模除 有些编译器为正 gcc为负数 171 | int x=0,y=0; 172 | get_n_pos(new_step,&x,&y); //传入x=y=0,传出新坐标 173 | //printf("x=%d,y=%d,pos=%d\n",x,y,new_step); 174 | set_p_pos(p,x,y); //更新玩家的坐标 175 | set_p_step(p,new_step); //更新玩家相对原点走过的步数 以索引land[] 176 | land=get_game_land(p,new_step); //玩家走到新位置 177 | set_land_symbol(land,get_p_symbol(p)); //设置新位置land上显示的符号 178 | set_land_color(land,get_p_color(p)); //设置地块颜色 179 | return 0; 180 | } 181 | 182 | int buy_land(st_game *game,st_land_info *land) 183 | { 184 | //计算土地价值 185 | int x=0,y=0; 186 | get_p_pos(game,&x,&y); 187 | //st_land_info *land=get_game_land(game,pos); 188 | int init_vlaue=get_map_value(game->p_map,x,y); 189 | init_vlaue*=100; 190 | if(get_land_price(land)!=0) init_vlaue=get_land_price(land); //购买已退出玩家的土地 191 | //买地 更新land map p->money 192 | int money=get_p_money(game); 193 | if( money < init_vlaue) 194 | { 195 | printf("Sorry,your money %d are not enough to buy land that worth %d\n",money,init_vlaue); 196 | return 1; 197 | } 198 | if(is_myturn(game)) 199 | printf("land worth is $%d, Do you want to buy land? Y-N\n",init_vlaue); 200 | 201 | if(is_yes()) 202 | { 203 | set_p_money(game,money-init_vlaue); 204 | set_land_player(land,get_player(game)); 205 | set_land_price(land,init_vlaue); 206 | set_land_ownclor(land,get_p_color(game)); 207 | print_name(game); 208 | printf(" spend %d on land\n",init_vlaue); 209 | } 210 | return 0; 211 | } 212 | 213 | int pay_rent(st_game *game,st_land_info *pland) 214 | { 215 | //计算应付租金 216 | //int step=get_p_step(game); 217 | //st_land_info *pland=get_game_land(game,step); 218 | st_player *player_pay=get_player(game); 219 | st_player *house_owner=get_land_player(pland); 220 | //p1->money-- p2->money++ 221 | int rent=get_land_price(pland)/2; 222 | int player_money=get_player_money(player_pay); 223 | 224 | printf("%s pay $%d to %s for rent\n",get_player_name(player_pay),\ 225 | rent,get_player_name(house_owner)); 226 | if(player_money < rent) 227 | { 228 | rent=player_money; 229 | printf("player %s bankrupt!\n",get_player_name(player_pay)); 230 | player_quit(game,get_player_id(player_pay)); 231 | } 232 | else 233 | { 234 | set_player_money(player_pay,player_money-rent); 235 | } 236 | 237 | set_player_money(house_owner,get_player_money(house_owner)+rent); 238 | return 0; 239 | 240 | //p1破产? bankrupt(game,id); 241 | } 242 | 243 | int game_over(st_game *game) 244 | { 245 | //只剩一人,游戏结束 246 | //int win_col=window_col(); 247 | char name[MAX_NAME_LEN+10]={0}; 248 | sprintf(name,"%s Win",get_p_name(game)); 249 | print_bar(name,'=',get_p_color(game)); 250 | //最后的玩家在table的0位置 251 | print_p_info(game,0); 252 | print_bar("Game Over!",'=',Red); 253 | deinit_game(&game); 254 | exit(0); 255 | } 256 | 257 | 258 | //传入的是st_player中的id 259 | void player_quit(st_game *game,int id) 260 | { 261 | //删除game中的指针 后继的指针前移 玩家数-- 262 | int player_num=get_game_pnum(game); 263 | st_player *player; 264 | st_land_info *pland=get_game_land(game,0); 265 | int i,j,player_id; 266 | char msg[MAX_NAME_LEN+64]; 267 | 268 | //在playertable中查找玩家 269 | for(i=0;ip_player_tab)[i]=((game)->p_player_tab)[i+1]; 297 | } 298 | set_game_pnum(game,--player_num); 299 | 300 | if(player_num == 1) 301 | game_over(game); 302 | 303 | } 304 | 305 | e_LandOwnerType whose_land(st_game *game,st_land_info *land) 306 | { 307 | int x,y; 308 | st_player *player=get_player(game); 309 | get_player_pos(player,&x,&y); 310 | char e=get_map_e(game->p_map,x,y); 311 | if(e==SHOP) return Toolshop; 312 | 313 | player=get_land_player(land); 314 | if(player == NULL) return Na; 315 | if(get_player_id(player)==get_p_id(game)) 316 | return Owner; 317 | else 318 | return Other; 319 | 320 | } 321 | int upgrade_house(st_game *game,st_land_info *land) 322 | { 323 | int p_money=get_p_money(game); 324 | int price=get_land_price(land); 325 | House_type level=get_land_level(land); 326 | if(p_money < price || level >= (MAX_LEVEL-1)) return 0; 327 | if(is_myturn(game)) 328 | printf("Do you want to upgrade your house? Y-N\n"); 329 | if(is_yes()) 330 | { 331 | set_land_level(land,level+1); 332 | set_land_price(land,price*2); 333 | set_p_money(game,p_money-price); 334 | printf("house level%d!\n",level+1); 335 | } 336 | return 0; 337 | } 338 | void print_game_info(st_player *player,int landnum) 339 | { 340 | print_player_info(player); 341 | printf(" land=%-3d",landnum); 342 | printf("\n"); 343 | } 344 | 345 | //打印player table[index]的玩家信息 id是player table中的索引 346 | void print_p_info(st_game *game,int index) 347 | { 348 | st_land_info *land=get_game_land(game,0); 349 | st_player *player; 350 | int land_num[MAX_PLAYER_NUM]; 351 | memset(land_num,0,sizeof(land_num)); 352 | int i; 353 | //计算每个玩家的土地数 354 | for(i=0;i10 || num <-10) goto retry; 543 | 544 | //设置地块显示 地块tool结构 在meettool时触发相关操作 545 | int step=get_p_step(game); 546 | int new_step=(num+step>=0) ? (num+step)%LAND_NUM : (LAND_NUM+(num+step)%LAND_NUM); 547 | 548 | st_land_info *land=get_game_land(game,new_step); 549 | 550 | int tool_num=get_land_tlnum(land); 551 | if(tool_num==MAX_TOOL_NUM) 552 | { 553 | printf("no room to put tools\n"); 554 | return; 555 | } 556 | set_land_symbol(land,tool_tb[blockID].symbol); 557 | set_land_color(land,tool_tb[blockID].color); 558 | add_land_tool(land,blockID); 559 | printf("player %s put block at %d\n",get_p_name(game),new_step); 560 | } 561 | 562 | //探索玩家的前n步是否有路障 返回遇到路障后走过的步数 563 | int explore_block(st_game *game,int n) 564 | { 565 | int current_step=get_p_step(game);//玩家当前步数 566 | st_land_info *land; 567 | int i; 568 | int onestep=n>=0?1:-1; 569 | int steps=0; 570 | int new_step=0; 571 | for(i=n>=0?n:-n ; i >=0 ; i--) 572 | { 573 | new_step=(steps+current_step>=0) ? (steps+current_step)%LAND_NUM : (LAND_NUM+(steps+current_step)%LAND_NUM); 574 | land=get_game_land(game,new_step); 575 | if(get_land_symbol(land) == tool_tb[blockID].symbol) 576 | { 577 | tool_tb[blockID].meettool(game,new_step); 578 | return steps; 579 | } 580 | steps+=onestep; 581 | } 582 | return n; 583 | } 584 | 585 | //如果地图上的symbol是'B' 就遇到路障 586 | void meet_Block(st_game *game,int n) 587 | { 588 | printf("you meet a block!\n"); 589 | } 590 | 591 | //和put_block meet_block的实现类似 592 | void NeedU2realize(st_game *game,int n) 593 | { 594 | printf("this function need you to reailize!\n"); 595 | } 596 | 597 | void next_player(st_game *game) 598 | { 599 | int next=(get_game_id(game)+1)%get_game_pnum(game); 600 | set_game_id(game,next); 601 | char des_buf[MAXLINE]={'x','\n'};//用字符'x'表示停止本次回合输入 602 | if(sockfd == -1) return; 603 | #if 1 604 | if(is_notice(sockfd)) //服务器通知 605 | { 606 | printf("next player\n"); 607 | Writen(sockfd, des_buf, 3); 608 | 609 | } 610 | download_cmd(sockfd,des_buf); 611 | clear_turnflag(game); 612 | #endif 613 | //client_run(sockfd,des_buf); 614 | } 615 | 616 | 617 | void print_land(st_game *p,int step) 618 | { 619 | st_land_info *land=get_game_land(p,step); 620 | print_color(get_land_symbol(land),land->color);//打印地块临时元素的颜色 621 | } 622 | 623 | 624 | //初始化地块信息 625 | void init_land(st_land_info (*p_land)[]) 626 | { 627 | 628 | int i; 629 | for(i=0;ip_player=p_player; 655 | } 656 | 657 | //获取土地的所有者 658 | st_player * get_land_player(st_land_info *land) 659 | { 660 | return land->p_player; 661 | } 662 | 663 | //设置土地颜色 664 | void set_land_color(st_land_info *land,e_color color) 665 | { 666 | land->color=color; 667 | } 668 | 669 | //获取土地颜色 670 | e_color get_land_color(st_land_info *land) 671 | { 672 | return land->color; 673 | } 674 | 675 | //设置土地价格 676 | void set_land_price(st_land_info *land,int price) 677 | { 678 | land->price=price; 679 | } 680 | 681 | int get_land_price(st_land_info *land) 682 | { 683 | return land->price; 684 | } 685 | 686 | //设置土地等级 687 | void set_land_level(st_land_info *land,House_type level) 688 | { 689 | land->level=level; 690 | } 691 | 692 | House_type get_land_level(st_land_info *land) 693 | { 694 | return land->level; 695 | } 696 | 697 | //获取第step块土地 698 | st_land_info * get_game_land(st_game *p,int step) 699 | { 700 | return p->p_land+step; 701 | } 702 | 703 | //设置土地临时显示符号 704 | void set_land_symbol(st_land_info *land,char symbol) 705 | { 706 | land->symbol=symbol; 707 | } 708 | 709 | char get_land_symbol(st_land_info *land) 710 | { 711 | return land->symbol; 712 | } 713 | 714 | //地块颜色和所有者一致 715 | e_color get_land_ownclor(st_land_info *land) 716 | { 717 | return land->owner_color; 718 | } 719 | 720 | void set_land_ownclor(st_land_info *land,e_color color) 721 | { 722 | land->owner_color=color; 723 | } 724 | 725 | //获取土地上道具的数量 726 | int get_land_tlnum(st_land_info *land) 727 | { 728 | return land->tool_n; 729 | } 730 | void set_land_tlnum(st_land_info *land,int num) 731 | { 732 | land->tool_n=num; 733 | } 734 | 735 | //设置土地上的第index的道具编号id 736 | void set_land_tln(st_land_info *land,int index,int id) 737 | { 738 | land->tools[index]=id; 739 | } 740 | int get_land_tln(st_land_info *land,int index) 741 | { 742 | return land->tools[index]; 743 | } 744 | 745 | //在土地上增加道具 746 | void add_land_tool(st_land_info *land,int id) 747 | { 748 | int num=get_land_tlnum(land); 749 | if(num == MAX_TOOL_NUM) printf("no room to put tools\n"); 750 | 751 | set_land_tln(land,num,id); 752 | set_land_tlnum(land,num+1); 753 | } 754 | 755 | 756 | /****************************对game结构体中player操作的封装**************************/ 757 | 758 | //获取玩家名字 linux下终端暂时不能显示中文 759 | char * get_p_name(st_game *p) 760 | { 761 | return get_player_name((p->p_player_tab)[p->id]); 762 | } 763 | 764 | //获取玩家id 765 | int get_p_id(st_game *p) 766 | { 767 | return get_player_id((p->p_player_tab)[p->id]); 768 | } 769 | 770 | //获取玩家地图上的位置 771 | void get_p_pos(st_game *p,int *x,int *y) 772 | { 773 | get_player_pos((p->p_player_tab)[p->id],x,y); 774 | } 775 | 776 | //设置玩家位置 777 | void set_p_pos(st_game *p,int x,int y) 778 | { 779 | set_player_pos((p->p_player_tab)[p->id],x,y); 780 | } 781 | 782 | //获取玩家金钱数 783 | int get_p_money(st_game *p) 784 | { 785 | return get_player_money((p->p_player_tab)[p->id]); 786 | } 787 | 788 | 789 | //设置玩家金钱 790 | void set_p_money(st_game *p,int money) 791 | { 792 | set_player_money((p->p_player_tab)[p->id],money); 793 | } 794 | 795 | //获取玩家地图上的标志 796 | char get_p_symbol(st_game *p) 797 | { 798 | return get_player_symbol((p->p_player_tab)[p->id]); 799 | } 800 | 801 | //获取玩家相对相对起点的步数 802 | int get_p_step(st_game *p) 803 | { 804 | return get_player_step((p->p_player_tab)[p->id]); 805 | } 806 | 807 | void set_p_step(st_game *p,int step) 808 | { 809 | set_player_step((p->p_player_tab)[p->id],step); 810 | } 811 | 812 | //获取玩家颜色 813 | e_color get_p_color(st_game *p) 814 | { 815 | return get_player_color((p->p_player_tab)[p->id]); 816 | } 817 | 818 | //获取当前玩家指针 819 | st_player *get_player(st_game *p) 820 | { 821 | return (p->p_player_tab)[p->id]; 822 | } 823 | 824 | //获取玩家表中第index个玩家的指针 825 | st_player *get_playern(st_game *p,int id) 826 | { 827 | return (p->p_player_tab)[id]; 828 | } 829 | 830 | //获取当前p_player_tab[]中的索引号 831 | int get_game_id(st_game *p) 832 | { 833 | return p->id; 834 | } 835 | 836 | //设置当前玩家表索引号 837 | void set_game_id(st_game *p,int id) 838 | { 839 | p->id=id; 840 | } 841 | 842 | //获取玩家数量 843 | int get_game_pnum(st_game *p) 844 | { 845 | return p->player_num; 846 | } 847 | 848 | //设置玩家数量 849 | void set_game_pnum(st_game *p,int num) 850 | { 851 | p->player_num=num; 852 | } 853 | 854 | //判断是否轮到自己 855 | int is_myturn(st_game *p) 856 | { 857 | if(sockfd==-1) return 1; 858 | return p->my_turnflag; 859 | } 860 | 861 | //清楚turn标志 862 | void clear_turnflag(st_game *p) 863 | { 864 | set_game_turn(p,0); 865 | } 866 | 867 | //设置turn标志 868 | void set_game_turn(st_game *p,int turn) 869 | { 870 | p->my_turnflag=turn; 871 | } 872 | 873 | //打印当前玩家名字 874 | void print_name(st_game *p) 875 | { 876 | print_color_str(get_p_name(p),get_p_color(p)); 877 | } 878 | 879 | 880 | 881 | -------------------------------------------------------------------------------- /src/input.txt: -------------------------------------------------------------------------------- 1 | g 1 2 | n 3 | g 2 4 | n 5 | g 3 6 | n 7 | g 4 8 | n 9 | e 10 | -------------------------------------------------------------------------------- /src/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include"map.h" 5 | 6 | char map[HEIGHT][WIDTH]= 7 | {{'s','1','2','3','4','5','6','7'}, 8 | {'#',' ',' ',' ',' ',' ','4','2'}, 9 | {'#',' ',' ',' ',' ',' ','4','2'}, 10 | {'#','n','n','n','n','n','4','2'}, 11 | {'@','3','3','3','3','3','3','@'}}; 12 | 13 | int value[HEIGHT][WIDTH]={{0,1,1,1,1,1,1,0}, 14 | {5,0,0,0,0,0,0,2}, 15 | {5,0,0,0,0,0,0,2}, 16 | {5,0,0,0,0,0,0,2}, 17 | {0,3,3,3,3,3,3,0}}; 18 | int House_value[MAX_LEVEL]={1000,2000,3000,4000}; 19 | 20 | 21 | //void print_map(char map[HEIGHT][WIDTH]); 22 | void get_next_pos(int *x,int *y); 23 | void get_prev_pos(int *x,int *y); 24 | 25 | #if 0 26 | int main() 27 | { 28 | //print_map(map); 29 | int x=0,y=0; 30 | char (*p)[WIDTH]=map; 31 | printf("%c",p[x][7]); 32 | 33 | st_map_info *p_map=init_map_info(); 34 | if(p_map==NULL) return; 35 | 36 | while(1) 37 | { 38 | get_n_pos(1,&x,&y); 39 | set_map_cur(p_map,x,y); 40 | set_map_e(p_map,'a',x,y); 41 | //printf("%c",p->map[x][y]); 42 | print_map(p_map); 43 | getchar(); 44 | } 45 | 46 | } 47 | #endif 48 | void print_map(st_map_info *p) 49 | { 50 | int i,j; 51 | for(i=0;imap[i][j]); 55 | printf("\n"); 56 | } 57 | } 58 | 59 | /* 60 | int x=0,y=0; 61 | while(1) 62 | { 63 | get_next_pos(&x,&y); 64 | printf("%c",map[x][y]); 65 | getchar(); 66 | } 67 | */ 68 | void get_next_pos(int *x,int *y) 69 | { 70 | if(*x==0 && *y!=(WIDTH-1)) 71 | (*y)++; 72 | else if(*y==(WIDTH-1) && *x!=(HEIGHT-1)) 73 | (*x)++; 74 | else if(*x==(HEIGHT-1) && *y!=0) 75 | (*y)--; 76 | else if(*y==0 && *x!=0) 77 | (*x)--; 78 | 79 | } 80 | 81 | void get_prev_pos(int *x,int *y) 82 | { 83 | if(*y==0 && *x!=(HEIGHT-1)) 84 | (*x)++; 85 | else if(*y!=(WIDTH-1) && *x==(HEIGHT-1)) 86 | (*y)++; 87 | else if(*x!=0 && *y==(WIDTH-1)) 88 | (*x)--; 89 | else if(*y!=0 && *x==0) 90 | (*y)--; 91 | 92 | } 93 | 94 | //从当前(x,y)向前或向后走n步 得到新的(x,y) 95 | void get_n_pos(int n,int *x,int *y) 96 | { 97 | if(n>=0) 98 | while((n--)>0) 99 | get_next_pos(x,y); 100 | else 101 | while((n++)<0) 102 | get_prev_pos(x,y); 103 | } 104 | 105 | //初始化地图 当前位置为(0,0) 106 | st_map_info * init_map_info() 107 | { 108 | st_map_info *p=NULL; 109 | p=malloc(sizeof(st_map_info)); 110 | if(p==NULL) return NULL; 111 | //p->map=map; 112 | memcpy(p->map,map,sizeof(map)); 113 | p->value=value; 114 | p->x=0; 115 | p->y=0; 116 | return p; 117 | } 118 | 119 | void deinit_map_info(st_map_info **p) 120 | { 121 | if(*p==NULL) return; 122 | free(*p); 123 | *p=NULL; 124 | } 125 | 126 | //设置地图上的当前位置 127 | void set_map_cur(st_map_info *p,int x,int y) 128 | { 129 | if(p->map[x][y]==' ' || x<0 ||x >=HEIGHT || 130 | y<0 || y>= WIDTH) return; //非法位置 设置失败 131 | p->x=x; 132 | p->y=y; 133 | } 134 | 135 | //获取地图当前位置 136 | void get_map_cru(st_map_info *p,int *x,int *y) 137 | { 138 | *x=p->x; 139 | *y=p->y; 140 | } 141 | 142 | //设置地图(x,y)元素的值 143 | void set_map_e(st_map_info *p,char e,int x,int y) 144 | { 145 | p->map[x][y]=e; 146 | } 147 | 148 | //获取位置的值 149 | char get_map_e(st_map_info *p,int x,int y) 150 | { 151 | return p->map[x][y]; 152 | } 153 | 154 | int get_map_value(st_map_info *p,int x,int y) 155 | { 156 | return (p->value)[x][y]; 157 | } -------------------------------------------------------------------------------- /src/out.txt: -------------------------------------------------------------------------------- 1 | Welcome to Richman! 2 | cmd summary(Shortcuts) 3 | roll: go random 1-6 steps. 4 | go N: go N steps. 5 | tool: display and use tool 6 | info: query player N's information. 7 | quitgame: player quit game. 8 | exitgame: end the game. 9 | A123H567 10 | # 2 11 | # 2 12 | # 2 13 | @333H33@ 14 | A Tubo id=0 pos=0 money=1000 tool=0 land=0 15 | A Tubo>g 1 16 | A Tubo go 1 steps. 17 | land worth is $100, Do you want to buy land? Y-N 18 | SA23H567 19 | # 2 20 | # 2 21 | # 2 22 | @333H33@ 23 | Sun Xiaomei id=1 pos=0 money=1000 tool=0 land=0 24 | Sun Xiaomei>g 2 25 | Sun Xiaomei go 2 steps. 26 | land worth is $100, Do you want to buy land? Y-N 27 | QAS3H567 28 | # 2 29 | # 2 30 | # 2 31 | @333H33@ 32 | Qian Furen id=2 pos=0 money=1000 tool=0 land=0 33 | Qian Furen>g 3 34 | Qian Furen go 3 steps. 35 | land worth is $100, Do you want to buy land? Y-N 36 | JASQH567 37 | # 2 38 | # 2 39 | # 2 40 | @333H33@ 41 | Jin Beibei id=3 pos=0 money=1000 tool=0 land=0 42 | Jin Beibei>g 4 43 | Jin Beibei go 4 steps. 44 | Id:0 G:tool name:Godwealth price:500 45 | Id:1 B:tool name:Block price:200 46 | Id:2 C:tool name:Control dice price:200 47 | Do you want to buy tools? Y-n 48 | sASQJ567 49 | # 2 50 | # 2 51 | # 2 52 | @333H33@ 53 | A Tubo id=0 pos=1 money=1000 tool=0 land=0 54 | A Tubo>e 55 | -------------------------------------------------------------------------------- /src/player.c: -------------------------------------------------------------------------------- 1 | #include"player.h" 2 | #include 3 | #include 4 | #include 5 | char *player_name[MAX_PLAYER_NUM]={"A Tubo","Sun Xiaomei","Qian Furen","Jin Beibei"}; 6 | char player_symbol[MAX_PLAYER_NUM]={'A','S','Q','J'}; 7 | e_color player_color[MAX_PLAYER_NUM]={Green,Red,Blue,Yellow}; 8 | #if 0 9 | int main() 10 | { 11 | st_player *p=init_player(0); 12 | print_player_info(p); 13 | deinit_player(&p); 14 | return 0; 15 | } 16 | #endif 17 | //初始化该id玩家 18 | st_player * init_player(int id) 19 | { 20 | st_player *p=malloc(sizeof(st_player)); 21 | if(p==NULL) return NULL; 22 | p->symbol=player_symbol[id]; 23 | p->name=player_name[id]; 24 | p->id=id; 25 | set_player_pos(p,0,0); 26 | set_player_step(p,0); 27 | set_player_money(p,INIT_MONEY); 28 | p->color=player_color[id]; 29 | init_player_tool(p); 30 | return p; 31 | } 32 | //销毁玩家 33 | void deinit_player(st_player **p) 34 | { 35 | if(*p==NULL) return; 36 | free(*p); 37 | *p=NULL; 38 | } 39 | 40 | //获取玩家名字 linux下终端暂时不能显示中文 41 | char * get_player_name(st_player *p) 42 | { 43 | return p->name; 44 | } 45 | 46 | //获取玩家id 47 | int get_player_id(st_player *p) 48 | { 49 | return p->id; 50 | } 51 | 52 | //获取玩家地图上的位置 53 | void get_player_pos(st_player *p,int *x,int *y) 54 | { 55 | *x=p->x; 56 | *y=p->y; 57 | } 58 | 59 | //设置玩家位置 60 | void set_player_pos(st_player *p,int x,int y) 61 | { 62 | p->x=x; 63 | p->y=y; 64 | } 65 | 66 | //获取玩家金钱数 67 | int get_player_money(st_player *p) 68 | { 69 | return p->money; 70 | } 71 | 72 | 73 | //设置玩家金钱 74 | void set_player_money(st_player *p,int money) 75 | { 76 | p->money=money; 77 | } 78 | 79 | //获取玩家地图上的标志 80 | char get_player_symbol(st_player *p) 81 | { 82 | return p->symbol; 83 | } 84 | 85 | //获取玩家相对相对起点的步数 86 | int get_player_step(st_player *p) 87 | { 88 | return p->step; 89 | } 90 | 91 | void set_player_step(st_player *p,int step) 92 | { 93 | p->step=step; 94 | } 95 | 96 | //获取玩家颜色 97 | e_color get_player_color(st_player *p) 98 | { 99 | return p->color; 100 | } 101 | //获取玩家道具数量 102 | int get_player_tlnum(st_player *p) 103 | { 104 | return p->tool_num; 105 | } 106 | //设置玩家道具数量 107 | void set_player_tlnum(st_player *p,int num) 108 | { 109 | p->tool_num=num; 110 | } 111 | //获取第index个道具的id 112 | int get_player_tln(st_player *p,int index) 113 | { 114 | return (p->tool_table)[index]; 115 | } 116 | //设置第index个道具的id 117 | void set_player_tln(st_player *p,int index,int id) 118 | { 119 | (p->tool_table)[index]=id; 120 | } 121 | //添加一个道具 道具table中id为MAX_TOOL_NUM表示空位置 122 | void add_player_tool(st_player *p,int id) 123 | { 124 | if(get_player_tlnum(p) == MAX_TOOL_NUM) return; 125 | int i=MAX_TOOL_NUM-1; 126 | for(;i>=0;i--) 127 | if(get_player_tln(p,i) == MAX_TOOL_NUM) 128 | { 129 | set_player_tln(p,i,id); 130 | break; 131 | } 132 | } 133 | void del_player_tool(st_player *p,int id) 134 | { 135 | int i=MAX_TOOL_NUM-1; 136 | for(;i>=0;i--) 137 | if(get_player_tln(p,i) == id) 138 | { 139 | set_player_tln(p,i,MAX_TOOL_NUM); 140 | set_player_tlnum(p,get_player_tlnum(p)-1); 141 | break; 142 | } 143 | } 144 | 145 | void init_player_tool(st_player *p) 146 | { 147 | set_player_tlnum(p,0); 148 | int i=MAX_TOOL_NUM-1; 149 | for(;i>=0;i--) 150 | set_player_tln(p,i,MAX_TOOL_NUM); 151 | 152 | } 153 | //打印玩家信息 154 | void print_player_info(st_player *p) 155 | { 156 | //int x,y; 157 | char *p_name=get_player_name(p); 158 | print_color_str(p_name,get_player_color(p)); 159 | 160 | int i=MAX_NAME_LEN-strlen(p_name); 161 | for(;i>0;i--) 162 | printf(" "); 163 | printf("id=%d",get_player_id(p)); 164 | printf(" pos=%-4d",get_player_step(p)); 165 | //printf(" pos:x=%d,y=%d",x,y); 166 | printf(" money=%-5d",get_player_money(p)); 167 | printf(" tool=%-2d",get_player_tlnum(p)); 168 | //printf(" symbol=%c\n",get_player_symbol(p)); 169 | } -------------------------------------------------------------------------------- /src/rich.c: -------------------------------------------------------------------------------- 1 | /* linux终端下C语言编程练习 大富翁游戏 可参考rich_jpeg.jpg中的数据结构 2 | * 在src目录下执行make, 执行./rich 运行游戏 网络版运行方式:打开两个终端,在终端1 3 | * 下的src目录下执行./rich -s ,在终端2下的src目录下执行./rich -c,终端1首先运行,然后 4 | * 终端2运行。 5 | * 6 | * 7 | * V0.1 完成一位玩家的行走,在land中用symbol保存临时的显示符号 打印地图时首先判断 8 | * 有无临时显示的元素。2012.11.22 9 | * V0.2 修改update_map函数,完成4人切换显示 在地图上显示玩家符号颜色,用于每次刷 10 | * 新land中的显示符号,程序框架基本确定 2012.11.22 11 | * V0.3 添加查询玩家信息函数player_info, 添加game中id及player_num相关函数,修改 12 | * update_map中的玩家数量的bug, 添加buy_land函数。2012.11.23 13 | * V0.4 添加单一玩家退出函数player_quit(),修正打印玩家信息时需要的id为玩家自身id, 14 | * 修改了deinit_game()中i的范围bug。 15 | * V0.5 在land中添加土地所有者的颜色owner_color,修改init_land(),print_game_map() 16 | * 修正玩家退出后仍显示符号的bug,修改print_game_map()中land显示bug。添加主函 17 | * 数文件rich.c。修改print_game_map(),使其在终端的中间打印地图。2012.11.25 18 | * V0.6 重新在util中写了print_bar代替了welcome(). 删除main中多余代码。 19 | * V0.7 添加roll函数 20 | * V0.8 重写cmd_go中的if-else为switch,暂时没想到更好的方式处理,静态表或树结构? 21 | * 添加upgrade_house函数。简化main函数,好像没啥好处。添加print_p_info 可查看 22 | * 玩家拥有的土地数量。修改退出时的显示 23 | * V0.9 添加tool相关功能,为玩家添加tool字段,购买道具,修改print_p_info的bug,重写 24 | * init_land(); 2012.11.30 25 | * V1.0 添加道具使用相关结构,和cmd的执行类似 26 | * V1.1 添加路障Block的使用和遇到Block的操作2012.12.04 27 | * V1.2 修改print_color函数,见tuil.h。添加道具使用的空函数,防止段错误 28 | * V1.3 修改 window_col()函数中获取标准输出的终端宽度,以防止输入重定向后不能得到标准 29 | * 输入的宽度的bug,测试用的input.txt文件中,若单行只有一个命令,后面要跟个空格, 30 | * 否则读的命令不正确。测试方式 ./rich.out < input.txt 重定向输出会错误 同样是 31 | * window_col()引起的,可#undef LINUX_CONSOLE进行输出重定向,使用diff对测试结果比 32 | * 较。bug:Block不能在有玩家的地块上使用。 33 | * V1.4 重写six_rand()函数。 34 | * V1.5 添加网络相关函数,在/net目录下,./a.out是服务器,首先运行,然后分别在另外两个 35 | * 终端下执行/src目录下的./rich.out -n,实现两位玩家的互动 ,实现客户端主要修改的 36 | * 函数有:get_cmd_line();get_num();next_player(发送字符x);init();is_yes(); 37 | * 添加全局变量sockfd, game结构体中添加turn标志位代表是否轮到自己。2012.12.14 38 | * V1.6 重写makefile,以同时make程序和服务器,写法还是不标准。启动服务器的方式:在init 39 | * 函数中使用fork和信号。ParseCmdLine函数确定程序的启动方式: 40 | * -server 服务器 -clinet 客户端 默认本地运行 41 | * 在init中#define PLAYER_NUM 2设置玩家数量,同时修改rich_server.c中的wait_client(2); 42 | * 运行时终端数量也要对应增加 2012.12.21 43 | * V1.7 修改4人时退出的bug,在rich_server.c中为write函数添加pipe信号捕获函数write_err 44 | * ,防止在write错误后程序直接终止。 45 | * V1.8 添加一些注释 46 | * V1.9 将fork中的tell_parent挪至execl启动后的进程中,去掉延时。加入选择玩家数量功能。 47 | * V2.0 修复行走负步数跨原点S时越界,和block道具使用中的类似bug。2013.1.24 48 | * open_gg@qq.com 49 | */ 50 | 51 | #include"console.h" 52 | 53 | 54 | int main(int argc, char *argv[]) 55 | { 56 | 57 | if(init(ParseCmdLine(argc,argv))<0) return -1; 58 | 59 | print_bar("Welcome to Richman!",' ',Green); 60 | cmd_help(0,NULL); 61 | while(1) 62 | { 63 | playing(); 64 | } 65 | 66 | } 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | int set_disp_mode(int fd,int option); 7 | #include 8 | #include 9 | #include 10 | #define MSG_TRY "try again\n" 11 | int g_timeout=0; 12 | int g_tstart=0; 13 | void select_sleep(int i); 14 | 15 | void *is_timeout(void *arg) 16 | { 17 | 18 | 19 | char buf[10]; 20 | int n; 21 | int flags; 22 | 23 | flags=fcntl(STDIN_FILENO,F_GETFL); 24 | flags|=O_NONBLOCK; 25 | if(fcntl(STDIN_FILENO,F_SETFL,flags)==-1){ 26 | perror("fcntl"); 27 | exit(1); 28 | } 29 | 30 | printf("time left:\n"); 31 | int i; 32 | char c; 33 | int num=0; 34 | printf("enter:\n"); 35 | printf("\033[s"); //保存光标 36 | printf("\033[?25l"); //隐藏光标 37 | system("stty raw -echo"); //变成单个字符输入模式 原模式为行输入模式 38 | set_disp_mode(STDIN_FILENO,0); 39 | g_tstart=1; 40 | #if 0 41 | for(i=9;i>=0;i--) 42 | { 43 | //printf("\033[2;11H");//把光标移动到第2行第11列 44 | //if(i==5) 45 | printf("\033[2;11H%d\n\033[u",i); 46 | //printf("\033[u"); 47 | //c=getc(stdin); 48 | //if(c=='\n') break; 49 | //printf("\033[2J"); 50 | select_sleep(2); 51 | } 52 | #endif 53 | while(g_tstart); 54 | system("stty -raw -echo"); 55 | set_disp_mode(STDIN_FILENO,1); 56 | printf("\033[2J"); //清屏 57 | printf("\033[?25h"); //显示光标 58 | g_timeout=1; 59 | } 60 | 61 | int main() 62 | { 63 | 64 | /*数组指针和指针数组*/ 65 | #if 0 66 | int a[5]={1,2,3,4,5}; 67 | int *p1=a; 68 | int (*p2)[]=&a; 69 | int *p3[5]; 70 | p3[0]=p1; 71 | int i; 72 | for(i=0;i<5;i++) 73 | { 74 | printf("%d ",*(*p2+i)); 75 | printf("\n"); 76 | } 77 | 78 | for(i=0;i<5;i++) 79 | { 80 | 81 | printf("%d ",*p3[i]); 82 | printf("\n"); 83 | } 84 | #endif 85 | 86 | 87 | /*getc测试 缓冲区的大小?*/ 88 | #if 0 89 | int i=2; 90 | int n=0; 91 | #define LINE_MAX 10 92 | char cmd_line[LINE_MAX]; 93 | memset(cmd_line,0,sizeof(cmd_line)); 94 | while(((cmd_line[n]=getc(stdin))!=EOF) && (cmd_line[n]!='\n') && ((++n) < LINE_MAX) ); 95 | cmd_line[n]='\0'; 96 | printf("%s\n",cmd_line); 97 | printf("n=%d\n",n); 98 | for(;n>=0;n--) 99 | printf("%d",cmd_line[n]); 100 | #endif 101 | 102 | /*非阻塞方式读取终端*/ 103 | #if 1 104 | 105 | #if 0 106 | tryagain: 107 | n=read(STDOUT_FILENO,buf,10); 108 | if(n<0){ 109 | if(errno==EAGAIN){ 110 | sleep(1); 111 | write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY)); 112 | goto tryagain; 113 | } 114 | perror("read stdin"); 115 | exit(1); 116 | } 117 | write(STDOUT_FILENO,buf,n); 118 | #endif // 119 | #endif //非阻塞读 120 | 121 | /*测试倒计时*/ 122 | #if 1 123 | 124 | char buf[10]; 125 | int n=0; 126 | int flags; 127 | 128 | flags=fcntl(STDIN_FILENO,F_GETFL); 129 | flags|=O_NONBLOCK; 130 | if(fcntl(STDIN_FILENO,F_SETFL,flags)==-1){ 131 | perror("fcntl"); 132 | exit(1); 133 | } 134 | 135 | printf("time left:\n"); 136 | 137 | char c; 138 | int num=0; 139 | printf("enter:\n"); 140 | printf("\033[s"); //保存光标 141 | printf("\033[?25l"); //隐藏光标 142 | system("stty raw echo"); //变成单个字符输入模式 原模式为行输入模式 143 | //set_disp_mode(STDIN_FILENO,0); 144 | g_tstart=1; 145 | 146 | 147 | 148 | 149 | pthread_t ptime; 150 | //pthread_create(&ptime, NULL,is_timeout,NULL); 151 | 152 | fd_set readfd; 153 | FD_ZERO(&readfd); 154 | FD_SET(STDOUT_FILENO,&readfd); 155 | 156 | struct timeval timeout,tpstart,tpend; 157 | 158 | gettimeofday(&tpstart,NULL); 159 | time_t tmp=tpstart.tv_sec*1000000+tpstart.tv_usec; 160 | 161 | int i=10; 162 | while(!g_timeout && i>0) 163 | { 164 | 165 | timeout.tv_sec = 1; 166 | timeout.tv_usec = 0; 167 | 168 | //select(STDOUT_FILENO+1, &readfd, NULL, NULL, & timeout ); 169 | //if(FD_ISSET(STDOUT_FILENO,&readfd)) 170 | n=read(STDOUT_FILENO,&num,1); 171 | if(n==1) 172 | printf("%d",num-'0'); 173 | //n=0; 174 | 175 | gettimeofday(&tpend,NULL); 176 | 177 | if((tpend.tv_sec*1000000+tpend.tv_usec -tmp )>1000000) 178 | { 179 | //select_sleep(1); 180 | printf("\033[2;11H%d\n\033[u",i--); 181 | gettimeofday(&tpstart,NULL); 182 | tmp=tpstart.tv_sec*1000000+tpstart.tv_usec; 183 | } 184 | } 185 | g_tstart=0; 186 | //pthread_join(ptime,NULL); 187 | 188 | 189 | system("stty -raw echo"); 190 | //set_disp_mode(STDIN_FILENO,1); 191 | printf("\033[2J"); //清屏 192 | printf("\033[?25h"); //显示光标 193 | g_timeout=1; 194 | 195 | #endif 196 | 197 | /*产生小随机数*/ 198 | #if 0 199 | #define x 3 200 | #define y 4 201 | #define NUM x+y 202 | int range=6; 203 | int ret; 204 | int i; 205 | int definenum=NUM; 206 | unsigned int bucket_size=RAND_MAX/range; 207 | srand((unsigned) time(NULL)); 208 | for(i=0;i<10;i++) 209 | { 210 | do ret=rand()/bucket_size+1; 211 | while(ret>range); 212 | printf("%d ",ret); 213 | } 214 | printf("\n"); 215 | #endif 216 | 217 | 218 | 219 | return 0; 220 | 221 | } 222 | 223 | 224 | int set_disp_mode(int fd,int option) 225 | { 226 | #include 227 | #include 228 | #include 229 | #include 230 | #define ECHOFLAGS (ECHO | ECHOE | ECHOK | ECHONL) 231 | 232 | int err; 233 | struct termios term; 234 | if(tcgetattr(fd,&term)==-1){ 235 | perror("Cannot get the attribution of the terminal"); 236 | return 1; 237 | } 238 | if(option) 239 | term.c_lflag|=ECHOFLAGS; 240 | else 241 | term.c_lflag &=~ECHOFLAGS; 242 | err=tcsetattr(fd,TCSAFLUSH,&term); 243 | if(err==-1 && err==EINTR){ 244 | perror("Cannot set the attribution of the terminal"); 245 | return 1; 246 | } 247 | return 0; 248 | } 249 | 250 | void select_sleep(int i) 251 | { 252 | struct timeval timeout; 253 | 254 | timeout.tv_sec = i; 255 | timeout.tv_usec = 0; 256 | 257 | select( 0, NULL, NULL, NULL, & timeout ); 258 | 259 | } 260 | 261 | 262 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include"util.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | void print_color(char c,e_color color) 11 | { 12 | #if 0 13 | switch(color) 14 | { 15 | case Green: 16 | printf("\033[1;40;32m%c\033[0m",c); break; 17 | case Red: 18 | printf("\033[1;40;31m%c\033[0m",c); break; 19 | case Blue: 20 | printf("\033[1;40;34m%c\033[0m",c); break; 21 | case Yellow: 22 | printf("\033[1;40;33m%c\033[0m",c); break; 23 | case None: 24 | default: 25 | printf("%c",c); 26 | } 27 | #endif 28 | printf("\033[1;40;%dm%c\033[0m",color,c); 29 | } 30 | 31 | 32 | void print_color_str(char *str,e_color color) 33 | { 34 | if(str==NULL) return; 35 | #if 0 36 | switch(color) 37 | { 38 | case Green: 39 | printf("\033[1;40;32m%s\033[0m",str); break; 40 | case Red: 41 | printf("\033[1;40;31m%s\033[0m",str); break; 42 | case Blue: 43 | printf("\033[1;40;34m%s\033[0m",str); break; 44 | case Yellow: 45 | printf("\033[1;40;33m%s\033[0m",str); break; 46 | case None: 47 | default: 48 | printf("%s",str); 49 | } 50 | #endif 51 | printf("\033[1;40;%dm%s\033[0m",color,str); 52 | } 53 | 54 | 55 | int is_digtial(char c) 56 | { 57 | if(c<='9' && c>='0') return 1; 58 | return 0; 59 | } 60 | 61 | int str2int(const char *str) 62 | { 63 | int ret=0; 64 | while((*str)!='\0' ) 65 | { 66 | if(!is_digtial(*str)) return ret; 67 | ret=ret*10+*str-'0'; 68 | str++; 69 | } 70 | return ret; 71 | } 72 | 73 | int find_int(char *str) 74 | { 75 | while(*str!='\0') 76 | { 77 | if(is_digtial(*str)) break; 78 | str++; 79 | } 80 | return str2int(str); 81 | } 82 | 83 | int window_col() 84 | { 85 | struct winsize size; 86 | ioctl(STDOUT_FILENO,TIOCGWINSZ,&size); 87 | //printf("%d\n",size.ws_col); 88 | return size.ws_col; 89 | } 90 | 91 | void print_line(char c,int num) 92 | { 93 | while(num-- > 0) 94 | printf("%c",c); 95 | } 96 | 97 | //在屏幕中间打印 98 | /*ccccstrcccc*/ 99 | void print_bar(char *str,char c,e_color color) 100 | { 101 | #ifdef LINUX_CONSOLE 102 | int win_col=window_col()/2; 103 | int str_len=strlen(str); 104 | int bar_len=win_col-str_len/2; 105 | while(bar_len-- > 0) 106 | printf("%c",c); 107 | print_color_str(str,color); 108 | bar_len=win_col-str_len+str_len/2; 109 | while(bar_len-- > 0) 110 | printf("%c",c); 111 | #else 112 | print_color_str(str,color); 113 | printf("\n"); 114 | #endif 115 | } 116 | 117 | int six_rand() 118 | { 119 | /*n很小或很大时,用%生成的随机数并不非常随机*/ 120 | #if 0 121 | static int n=0; 122 | if(n==0) 123 | srand((unsigned) time(NULL)); 124 | if(n++>0xffff)n=0; 125 | int ret=rand(); 126 | //ret=(ret>>4)&0xf; 127 | return (ret+n)%6+1; 128 | #endif 129 | int range=6,ret; 130 | 131 | /*将随机数区间分割成相同大小的桶*/ 132 | unsigned int bucket_size=RAND_MAX/range; 133 | srand((unsigned) time(NULL)); 134 | 135 | /*得到桶的编号,区间外的编号舍弃*/ 136 | do ret=rand()/bucket_size+1; 137 | while(ret > range); 138 | return ret; 139 | } 140 | 141 | void *is_timeout(void *arg) 142 | { 143 | printf("time left:\n"); 144 | int i; 145 | char c; 146 | printf("enter:"); 147 | for(i=9;i>=0;i--) 148 | { 149 | printf("\033[2;11H");//把光标移动到第2行第11列 150 | printf("%d\n",i); 151 | printf("\033[3;7H"); 152 | //c=getc(stdin); 153 | //if(c=='\n') break; 154 | //printf("\033[2J"); 155 | sleep(1); 156 | } 157 | printf("\033[2J");//清屏 158 | } 159 | #if 0 160 | #include 161 | int main() 162 | { 163 | char *p="1234fd3"; 164 | print_color(*p,Red); 165 | print_color_str(p,Red); 166 | printf("%d\n",str2int(p)); 167 | window_col(); 168 | int i; 169 | for(i=0;i<20;i++) 170 | printf("%d ",six_rand());} 171 | 172 | #endif 173 | 174 | -------------------------------------------------------------------------------- /test case/case1.txt: -------------------------------------------------------------------------------- 1 | g 1 2 | n 3 | g 2 4 | n 5 | g 3 6 | n 7 | g 4 8 | n 9 | e --------------------------------------------------------------------------------