/* $Id: animal.c,v 2.1 2006/04/17 07:22:19 starfish Exp $ */ #include "bbs.h" typedef struct point_struct { int x; int y; } point_t; typedef char animal_board_t[7][9]; typedef struct animal_struct { char board[7][9]; char user[2][IDLEN+1]; int side; point_t pickup; int flip; } animal_t; static const char *animal_name[9] = { "穴", "象", "獅", "虎", "豹\", "狗", "狼", "貓", "鼠" }; static void indigestion(int i) { fprintf(stderr, "嚴重內傷 %d\n", i); } static void animal_write_header(FILE * fp, animal_t *sys, int reply) { char *ptr; struct { char author[IDLEN + 1]; char board[IDLEN + 1]; char title[66]; time_t date; /* last post's date */ int number; /* post number */ } postlog; fprintf(fp, "%s %s (%s)\n", str_author1, cuser->userid, cuser->username); memset(&postlog, 0, sizeof(postlog)); strlcpy(postlog.author, cuser->userid, sizeof(postlog.author)); strlcpy(postlog.board, currboard, sizeof(postlog.board)); if (reply) { snprintf(save_title, sizeof(save_title), "Re: [鬥獸] %s 對 %s", sys->user[0], sys->user[1]); } else { snprintf(save_title, sizeof(save_title), "[鬥獸] %s 對 %s", sys->user[0], sys->user[1]); } ptr = save_title; strncpy(postlog.title, ptr, 65); postlog.date = now; postlog.number = 1; append_record(".post", (fileheader_t *) & postlog, sizeof(postlog)); save_title[72] = '\0'; fprintf(fp, "標題: %s\n時間: %s\n", save_title, ctime(&now)); } static point_t flip_point(point_t point) { point_t result = { 6-point.x, 8-point.y }; return result; } static char * board_access(animal_t *sys, point_t loc) { point_t fliped = flip_point(loc); if (!sys->flip) return &sys->board[loc.x][loc.y]; else return &sys->board[fliped.x][fliped.y]; } static int in_river(point_t loc) { return (loc.x==1 || loc.x==2 || loc.x==4 || loc.x==5) && (loc.y>=3 && loc.y<=5); } static void concat_grid_string(animal_t *sys, point_t loc, char *buffer) { int animal = *board_access(sys, loc); if (animal != 0) { if (loc.x==sys->pickup.x && loc.y==sys->pickup.y) { strcat(buffer, "\033[7m#"); strcat(buffer, animal_name[abs(animal)]); strcat(buffer, "#\033[0m"); } else { strcat(buffer, animal>0 ? "\033[1;31m<" : "\033[1;32m["); strcat(buffer, animal_name[abs(animal)]); strcat(buffer, animal>0 ? ">\033[0m" : "]\033[0m"); } } else if (loc.x==3 && ((loc.y==0 && !sys->flip) || (loc.y==8 && sys->flip))) { strcat(buffer, "\033[1;32m[穴]\033[0m"); } else if (loc.x==3 && ((loc.y==8 && !sys->flip) || (loc.y==0 && sys->flip))) { strcat(buffer, "\033[1;31m<穴>\033[0m"); } else if (((loc.y==0 || loc.y==8) && (loc.x==2 || loc.x==4)) || ((loc.y==1 || loc.y==7) && loc.x==3)) { strcat(buffer, "陷阱"); } else if (in_river(loc)) { strcat(buffer, "\033[44m∼∼\033[0m"); } else { strcat(buffer, " "); } } static void return_line(animal_t *sys, int line, char buffer[246]) { /* ┌──┬──┬──┬──┬──┬──┬──┐ [綠] PlayerB │[獅]│ │ 阱 │ 穴 │ 阱 │ │[虎]│ ├──┼──┼──┼──┼──┼──┼──┤ │ │[狗]│ │ 阱 │ │[貓]│ │ ├──┼──┼──┼──┼──┼──┼──┤ │[鼠]│ │[豹]│ │[狼]│ │[象]│ ├──┌──┼──┐──┌──┼──┐──┤ │ │ ∼ │ ∼ │ │ ∼ │ ∼ │ │ ├──├──┼──┤──├──┼──┤──┤ 現在輪到<紅>獸移動 │ │ ∼ │ ∼ │ │ ∼ │ ∼ │ │ ├──├──┼──┤──├──┼──┤──┤ │ │ ∼ │ ∼ │ │ ∼ │ ∼ │ │ ├──└──┼──┘──└──┼──┘──┤ │<象>│ │<狼>│ │<豹>│ │<鼠>│ ├──┼──┼──┼──┼──┼──┼──┤ │ │<貓>│ │ 阱 │ │<狗>│ │ ├──┼──┼──┼──┼──┼──┼──┤ │<虎>│ │ 阱 │ 穴 │阱 │ │<獅>│ └──┴──┴──┴──┴──┴──┴──┘ <紅> PlayerA */ int i; if (line >= 19) { buffer[0] = 0; } else if ((line & 1) == 0) { static const char *grid[] = { "┌──┬──┬──┬──┬──┬──┬──┐", "├──┼──┼──┼──┼──┼──┼──┤", "├──┼──┼──┼──┼──┼──┼──┤", "├──┌──┼──┐──┌──┼──┐──┤", "├──├──┼──┤──├──┼──┤──┤", "├──├──┼──┤──├──┼──┤──┤", "├──└──┼──┘──└──┼──┘──┤", "├──┼──┼──┼──┼──┼──┼──┤", "├──┼──┼──┼──┼──┼──┼──┤", "└──┴──┴──┴──┴──┴──┴──┘" }; strcpy(buffer, grid[line>>1]); } else { strcpy(buffer, "│"); for (i=0; i<7; ++i) { point_t loc = { i, line>>1 }; concat_grid_string(sys, loc, buffer); strcat(buffer, "│"); } } if ((line==0 && !sys->flip) || (line==18 && sys->flip)) { strcat(buffer, " \033[1;32m[綠]\033[0m "); strcat(buffer, sys->user[1]); } else if ((line==18 && !sys->flip) || (line==0 && sys->flip)) { strcat(buffer, " \033[1;31m<紅>\033[0m "); strcat(buffer, sys->user[0]); } else if (line==8) { if (sys->side > 0) { strcat(buffer, " 現在輪到\033[1;31m<紅>\033[0m獸移動"); } else { strcat(buffer, " 現在輪到\033[1;32m[綠]\033[0m獸移動"); } } } static int animal_write_file(animal_t *sys, int reply, char *fpath, int *islocal) { int fd; FILE *fp = NULL; char buffer[246]; int i; if (!*fpath) { sethomepath(fpath, cuser->userid); strcat(fpath, "ve_XXXXXXXX"); if ((fd = mkstemp(fpath)) == -1) return -1; if ((fp = fdopen(fd, "w")) == NULL) return -1; } else if ((fp = fopen(fpath, "w")) == NULL) { indigestion(3); abort_bbs(0); } animal_write_header(fp, sys, reply); for (i=0; i<19; ++i) { int old_flip = sys->flip; point_t old_pickup = sys->pickup; sys->flip = 0; sys->pickup.x = -1; sys->pickup.y = -1; return_line(sys, i, buffer); fprintf(fp, "%s\n", buffer); sys->flip = old_flip; sys->pickup = old_pickup; } fclose(fp); return 0; } static void new_board(animal_t *sys) { int i; int j; for (i=0; i<7; i++) for (j=0; j<9; j++) sys->board[i][j] = 0; sys->board[0][6] = 1; sys->board[6][8] = 2; sys->board[0][8] = 3; sys->board[4][6] = 4; sys->board[5][7] = 5; sys->board[2][6] = 6; sys->board[1][7] = 7; sys->board[6][6] = 8; sys->board[6][2] = -1; sys->board[0][0] = -2; sys->board[6][0] = -3; sys->board[2][2] = -4; sys->board[1][1] = -5; sys->board[4][2] = -6; sys->board[5][1] = -7; sys->board[0][2] = -8; } static int string_shorter_than(char *str, int len) { int i; for (i=0; i>1; for (x=0, j=0; x<7; ++x) { if (string_shorter_than(line+j, 2)) goto fail; j += 2; /* drop a grid line */ if (line[j] == '\033') { if (string_shorter_than(line+j, 7+4+3)) goto fail; j += jump_over_colour_code(line+j); if (string_shorter_than(line+j, 4+3)) goto fail; if (line[j] == '<') side=1; else if (line[j] == '[') side=-1; else { sys->board[x][y] = 0; j += 4; j += jump_over_colour_code(line+j); continue; } animal = find_animal_name(line+j+1) * side; sys->board[x][y] = animal; j += 4; j += jump_over_colour_code(line+j); } else { if (string_shorter_than(line+j, 4)) goto fail; sys->board[x][y] = 0; j += 4; } } } if (i==0) { strlcpy(sys->user[1], line+62, sizeof sys->user[1]); trim_first(sys->user[1]); } else if (i==18) { strlcpy(sys->user[0], line+62, sizeof sys->user[0]); trim_first(sys->user[0]); } else if (i==8) { if (strlen(line) != 76) goto fail; if (line[61] == '<') sys->side = 1; else if (line[61] == '[') sys->side = -1; else goto fail; } } fclose(fp); return; fail: fclose(fp); new_board(sys); } static void display_board(animal_t *sys) { auto char line[246]; int i; move(0, 0); for (i=0; i<19; i++) { clrtoeol(); return_line(sys, i, line); prints("%s\n", line); } move(23, 0); clrtoeol(); prints("%s", " 按 [\033[1;32m2846\033[0m] 控制方向" " [\033[1;32mSPACE\033[0m] 選擇及移動棋子" " [\033[1;32mT\033[0m] 反轉棋盤" " [\033[1;32mS\033[0m] 確定" " [\033[1;32mQ\033[0m] 取消"); } static void local_repaint(animal_t *sys, point_t loc) { /* char buffer[32]; move(loc.y*2+1, loc.x*6+2); buffer[0] = 0; concat_grid_string(sys, loc, buffer); prints("%s", buffer); */ int line = loc.y*2+1; char buffer[246]; return_line(sys, line, buffer); move(line, 0); clrtoeol(); prints("%s", buffer); } static int in_trap(int animal, point_t loc, int flipped) { if ((animal>0 && !flipped) || (animal<0 && flipped)) { if ( (loc.x==2 && loc.y==0) || (loc.x==3 && loc.y==1) || (loc.x==4 && loc.y==0)) return 1; else return 0; } else { if ( (loc.x==2 && loc.y==8) || (loc.x==3 && loc.y==7) || (loc.x==4 && loc.y==8)) return 1; else return 0; } } static int legal_move(animal_t *sys, point_t dst) { /* 棋規: 棋子大子順序為:象>獅>虎>豹>狗>狼>貓>鼠。 大子吃小子,同類能互吃,例外者為鼠吃象而象不能吃鼠。 每次走一格,前後左右皆可,但除鼠以外其他獸不能入河。 鼠上落河不能吃棋。 獅和虎可以跳河,橫直皆可,唯跳河路線不能有任何棋子阻隔。 獅和虎跳河可以吃掉目的地的較弱小的獸。 進入對方陷阱的獸即失去戰鬥力,可以被任何在旁的敵獸吃掉。 入侵對方獸穴者勝,自己一方的獸不能進入自己獸穴。 */ point_t thr = sys->pickup; int animal, aanimal, animal2; if (thr.x<0 || thr.y<0) return 0; /* 沒有選取棋子 */ animal = *board_access(sys, thr); if (!animal) return 0; /* 選取了空地 */ if (animal*sys->side < 0) return 0; /* 選了別人的棋子 */ if ((animal>0 && !sys->flip) || (animal<0 && sys->flip)) { if (dst.x==3 && dst.y==8) return 0; /* 紅獸入自己獸穴 */ } else { if (dst.x==3 && dst.y==0) return 0; /* 綠獸入自己獸穴 */ } aanimal = abs(animal); if (abs(dst.x - thr.x) + abs(dst.y - thr.y) > 1) { if (aanimal!=2 && aanimal!=3) return 0; if (thr.y>=3 && thr.y<=5) { /* 橫跳 */ int x1, x2; point_t p; if (dst.y != thr.y) return 0; if (abs(dst.x-thr.x) != 3) return 0; x1 = MIN(thr.x, dst.x); x2 = MAX(thr.x, dst.x); p.y = thr.y; for (p.x=x1+1; p.x 0) return 0; /* 自己人不打自己人 */ if (!in_trap(animal2, dst, sys->flip)) { int aanimal2 = abs(animal2); if (aanimal==1 && aanimal2==8) return 0; /* 象不吃鼠 */ if ((aanimal>aanimal2) && (aanimal!=8 || aanimal2!=1)) return 0; /* 小不吃大,除非鼠吃象 */ if (in_river(thr) != in_river(dst)) return 0; /* 上落河不能吃子 */ } } return 1; } /* 主程式、鍵盤處理 */ int vedit_animal(char *fpath, int saveheader, int *islocal) { animal_t sys; int reply = 0; int err_msg_shown = 0; int ch; point_t cur = { 3, 4 }; int played = 0; int check_rule = 1; sys.side = 1; sys.pickup.x = -1; sys.pickup.y = -1; sys.flip = 0; new_board(&sys); strcpy(sys.user[0], "???"); strcpy(sys.user[1], "???"); if (*quote_file) { reply = 1; import_animal(&sys); if (sys.side < 0) sys.flip = 1; } clear(); display_board(&sys); for (;;) { move(cur.y*2+1, cur.x*6+3); oflush(); ch = igetkey(); if (err_msg_shown) { /* clear error message after key pressed */ move(20,0); clrtoeol(); } switch (ch) { case KEY_UP : case '8' : local_repaint(&sys, cur); --cur.y; if (cur.y < 0) cur.y = 8; break; case KEY_DOWN : case '2' : local_repaint(&sys, cur); ++cur.y; if (cur.y > 8) cur.y = 0; break; case KEY_LEFT : case '4' : local_repaint(&sys, cur); --cur.x; if (cur.x < 0) cur.x = 6; break; case KEY_RIGHT : case '6' : local_repaint(&sys, cur); ++cur.x; if (cur.x > 6) cur.x = 0; break; case ' ' : case '5' : if (sys.pickup.x == -1 || sys.pickup.y == -1) { int chequer = *board_access(&sys, cur) * sys.side; if (!chequer) break; if (check_rule && chequer < 0) { move(20,0); clrtoeol(); prints("你不能移對方的棋子"); err_msg_shown = 1; break; } sys.pickup.x = cur.x; sys.pickup.y = cur.y; display_board(&sys); if (check_rule && played) { move(20,0); clrtoeol(); prints("注意:你在這次編輯中下了超過一步棋"); err_msg_shown = 1; } } else { int chequer = *board_access(&sys, sys.pickup); if (check_rule && !legal_move(&sys, cur)) { move(20,0); clrtoeol(); prints("犯規的移動"); err_msg_shown = 1; sys.pickup.x = -1; sys.pickup.y = -1; display_board(&sys); break; } *board_access(&sys, sys.pickup) = 0; *board_access(&sys, cur) = chequer; sys.pickup.x = -1; sys.pickup.y = -1; played = 1; if (sys.side == 1) strcpy(sys.user[0], currutmp->userid); else strcpy(sys.user[1], currutmp->userid); sys.side = -sys.side; display_board(&sys); } break; case 'T': case 't': sys.flip = !sys.flip; if (sys.pickup.x>=0 && sys.pickup.y>=0) sys.pickup = flip_point(sys.pickup); display_board(&sys); break; case 'L': case 'l': check_rule = !check_rule; break; case 'O': case 'o': if (!check_rule) { new_board(&sys); display_board(&sys); } break; case 'S': case 's': if (check_rule && played) { return animal_write_file(&sys, reply, fpath, islocal); } else { move(20,0); clrtoeol(); prints("你未下棋不能儲存"); err_msg_shown = 1; } break; case 'Q': case 'q': return -1; } } return -1; }