首页 文章 精选 留言 我的

精选列表

搜索[小游戏],共537篇文章
优秀的个人博客,低调大师

2048小游戏

废话不多,直接上源码 #include<time.h> #include<stdlib.h> #include<conio.h> #include<stdio.h> #include<windows.h> #define x0 26 #define y0 1 HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); /*显示声明*/ void p(int*a, int i, int x, int y); /*部分刷新声明*/ void shua(void); /*胜利画面测试函数声明*/ int test(void); /*字体空格声明*/ void kg(int*a); /*画长条函数的声明*/ void ct(int x, int y, int l, int k, int c); /*标题字幕2048*/ void p2048(int x, int y); /*单个颜色返回值声明*/ int col(int*a); /*移动光标*/ void gotoxy(int x, int y) { COORD coordScreen = { 0,0 }; coordScreen.X = x; coordScreen.Y = y; SetConsoleCursorPosition(hOutput, coordScreen); } /*字体颜色*/ void textcolor(int color) { SetConsoleTextAttribute(hOutput, color); } /*显示函数*/ void prin(int*a, int*score, int*scoremax) { int i; for (i = 0; i < 16; i++) { p(a, i, x0, y0); } printf("\n\n score=%d MAXscore=%d \n", *score, *scoremax); } /* 产生随机数 */ int shu(void) { int s = 0; s = 1 + (int)(12.0 * rand() / (RAND_MAX + 1.0)); if (s == 12) return 4; else return 2; } /* 决定位置 */ int rands(void) { return (int)(16.0 * rand() / (RAND_MAX + 1.0)); } /* 主函数*/ int main(void) { int flag = 0, c = 0, pd = 1, n = 0, i = 0, m = 0, j = 0, sj, sj1, sj2, a[16] = { 0 }, b[16] = { 0 }, x = 0, score = 0, t = 0, jx = 0, scoremax = 0, ks = 1, yx = 0; char sr, an; //开始画面 p2048(17, 1); textcolor(255); gotoxy(35, 15); for (;;) { an = getch(); switch (an) { case 72: if (ks == 1) ks = 0; else ks = 1; break; case 80: if (ks == 1) ks = 0; else ks = 1; break; case '\r': yx = 1; break; default: break; } if (ks == 1) gotoxy(35, 15); else gotoxy(34, 18); if (ks == 1 && yx == 1) break; else if (ks == 0 && yx == 1) { textcolor(240); gotoxy(0, 0); for (i = 0; i <= 500; i++) { printf(" "); } gotoxy(0, 0); ct(12, 5, 58, 10, 223); gotoxy(13, 6); printf(" 2048游戏说明"); gotoxy(13, 8); printf(" 通过按数字键移动数字,合并相同的数字来的取得更大的数。"); gotoxy(13, 9); printf(" 得到2048时游戏即胜利,当然您也可以选择继续挑战。"); gotoxy(13, 11); printf(" 按键操作:"); gotoxy(13, 13); printf(" 上:↑ 下:↓ 左:← 右:→ 重新开始:r 退出游戏:e"); textcolor(240); printf("\n\n\n\n\n I know "); getch(); p2048(17, 1); textcolor(255); gotoxy(34, 18); } yx = 0; } //隐藏光标 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cci; GetConsoleCursorInfo(hOut, &cci); cci.bVisible = FALSE; SetConsoleCursorInfo(hOut, &cci); //刷新画面 textcolor(240); gotoxy(0, 0); for (i = 0; i <= 500; i++) { printf(" "); } gotoxy(0, 0); /*设置时间种子*/ srand((int)time(0)); /* 设置初始值 */ sj = rands(); sj1 = rands(); sj2 = rands(); for (;;) { if (sj == sj1 || sj == sj2 || sj1 == sj2) { sj1 = rands(); sj2 = rands(); } else break; } a[sj] = shu(); a[sj1] = shu(); a[sj2] = shu(); /*显示*/ prin(a, &score, &scoremax); for (;;) { /* 无回显输入 */ sr = getch(); /* scanf("%d",&n); *//* 记录之前的位置,将用以比较移动是否有效 */ for (j = 0; j < 16; j++) b[j] = a[j]; switch (sr) { /* 向上移动 */ case 72: /* 全部移到上边 */ for (j = 0; j < 4; j++) { for (c = 0; c < 3; c++) { if (a[j] == 0) { a[j] = a[j + 4]; a[j + 4] = 0; } if (a[j + 4] == 0) { a[j + 4] = a[j + 8]; a[j + 8] = 0; } if (a[j + 8] == 0) { a[j + 8] = a[j + 12]; a[j + 12] = 0; } } /* 移到上面后的合并操作 */ if (a[j] == a[j + 4]) { a[j] = a[j] * 2; score = score + a[j]; a[j + 4] = a[j + 8]; a[j + 8] = a[j + 12]; a[j + 12] = 0; if (a[j + 4] == a[j + 8]) { a[j + 4] = 2 * a[j + 4]; score = score + a[j + 4]; a[j + 8] = 0; } } else if (a[j + 4] == a[j + 8]) { a[j + 4] = 2 * a[j + 4]; score = score + a[j + 4]; a[j + 8] = a[j + 12]; a[j + 12] = 0; } else if (a[j + 8] == a[j + 12]) { a[j + 8] = 2 * a[j + 8]; score = score + a[j + 8]; a[j + 12] = 0; } else; } break; /* 向下移动 */ case 80: /* 全部移动到下面 */ for (j = 12; j < 16; j++) { for (c = 0; c < 3; c++) { if (a[j] == 0) { a[j] = a[j - 4]; a[j - 4] = 0; } if (a[j - 4] == 0) { a[j - 4] = a[j - 8]; a[j - 8] = 0; } if (a[j - 8] == 0) { a[j - 8] = a[j - 12]; a[j - 12] = 0; } } /* 移到下面后的合并操作 */ if (a[j] == a[j - 4]) { a[j] = a[j] * 2; score = score + a[j]; a[j - 4] = a[j - 8]; a[j - 8] = a[j - 12]; a[j - 12] = 0; if (a[j - 4] == a[j - 8]) { a[j - 4] = 2 * a[j - 4]; score = score + a[j - 4]; a[j - 8] = 0; } } else if (a[j - 4] == a[j - 8]) { a[j - 4] = 2 * a[j - 4]; score = score + a[j - 4]; a[j - 8] = a[j - 12]; a[j - 12] = 0; } else if (a[j - 8] == a[j - 12]) { a[j - 8] = 2 * a[j - 8]; score = score + a[j - 8]; a[j - 12] = 0; } else; } break; /* 向左移动 */ case 75: /* 全部移动到左面 */ for (j = 0; j <= 12; j = j + 4) { for (c = 0; c < 3; c++) { if (a[j] == 0) { a[j] = a[j + 1]; a[j + 1] = 0; } if (a[j + 1] == 0) { a[j + 1] = a[j + 2]; a[j + 2] = 0; } if (a[j + 2] == 0) { a[j + 2] = a[j + 3]; a[j + 3] = 0; } } /* 移到左面后的合并操作 */ if (a[j] == a[j + 1]) { a[j] = a[j] * 2; score = score + a[j]; a[j + 1] = a[j + 2]; a[j + 2] = a[j + 3]; a[j + 3] = 0; if (a[j + 1] == a[j + 2]) { a[j + 1] = 2 * a[j + 1]; score = score + a[j + 1]; a[j + 2] = 0; } } else if (a[j + 1] == a[j + 2]) { a[j + 1] = 2 * a[j + 1]; score = score + a[j + 1]; a[j + 2] = a[j + 3]; a[j + 3] = 0; } else if (a[j + 2] == a[j + 3]) { a[j + 2] = 2 * a[j + 2]; score = score + a[j + 2]; a[j + 3] = 0; } else; } break; /* 向右移动 */ case 77: /* 全部移动到右面 */ for (j = 3; j <= 16; j = j + 4) { for (c = 0; c < 3; c++) { if (a[j] == 0) { a[j] = a[j - 1]; a[j - 1] = 0; } if (a[j - 1] == 0) { a[j - 1] = a[j - 2]; a[j - 2] = 0; } if (a[j - 2] == 0) { a[j - 2] = a[j - 3]; a[j - 3] = 0; } } /* 移到右面后的合并操作 */ if (a[j] == a[j - 1]) { a[j] = a[j] * 2; score = score + a[j]; a[j - 1] = a[j - 2]; a[j - 2] = a[j - 3]; a[j - 3] = 0; if (a[j - 1] == a[j - 2]) { a[j - 1] = 2 * a[j - 1]; score = score + a[j - 1]; a[j - 2] = 0; } } else if (a[j - 1] == a[j - 2]) { a[j - 1] = 2 * a[j - 1]; score = score + a[j - 1]; a[j - 2] = a[j - 3]; a[j - 3] = 0; } else if (a[j - 2] == a[j - 3]) { a[j - 2] = 2 * a[j - 2]; score = score + a[j - 2]; a[j - 3] = 0; } else; } break; /* 重新开始 */ case 'r': for (j = 0; j < 16; j++) a[j] = 0; sj1 = rands(); sj2 = rands(); for (;;) { if (sj1 == sj2) { sj1 = rands(); sj2 = rands(); } else break; } a[sj1] = shu(); a[sj2] = shu(); flag = 0; score = 0; jx = 0; break; /* 退出 */ case 'e': shua(); ct(32, 19, 15, 3, 207); printf(" 游戏已退出!"); textcolor(255); gotoxy(0, 0); getch(); return 0; break; /* 其他情况(刷新) */ default: ; break; } /*windows下的系统清屏函数*/ //system ("cls"); /* 判断最高分 */ if (score > scoremax) scoremax = score; /* 判断是否胜利(是否含有2048) */ for (j = 0; j < 16; j++) { if (a[j] == 2048 && jx == 0) { /* 胜利时输出游戏结果 */ /*显示*/ prin(a, &score, &scoremax); shua(); ct(20, 19, 15, 3, 207); printf(" 游戏胜利!"); textcolor(159); gotoxy(38, 19); printf(" Continue "); textcolor(239); gotoxy(49, 20); printf(" Restart "); textcolor(175); gotoxy(49, 21); printf(" Exit "); t = 2; an = 75; yx = 0; for (;;) { an = getch(); switch (an) { case 72: if (t == 2) { t = 3; textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(38, 21); printf(" Exit "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(47, 19); printf(" Continue "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(49, 20); printf(" Restart "); } else if (t == 1) { t = 2; textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(38, 19); printf(" Continue "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(47, 20); printf(" Restart "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(49, 21); printf(" Exit "); } else { t = 1; textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(38, 20); printf(" Restart "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(47, 21); printf(" Exit "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(49, 19); printf(" Continue "); } break; case 80: if (t == 2) { t = 1; textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(38, 20); printf(" Restart "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(47, 19); printf(" Continue "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(49, 21); printf(" Exit "); } else if (t == 1) { t = 3; textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(38, 21); printf(" Exit "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(47, 20); printf(" Restart "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(49, 19); printf(" Continue "); } else { t = 2; textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(38, 19); printf(" Continue "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(47, 21); printf(" Exit "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(49, 20); printf(" Restart "); } break; case '\r': yx = 1; break; } if (yx == 1) { yx = 0; break; } } shua(); textcolor(255); gotoxy(0, 0); /* 重新开始游戏 */ if (t == 1) { shua(); pd = 1; x = 0; for (j = 0; j < 16; j++) a[j] = 0; sj1 = rands(); sj2 = rands(); for (;;) { if (sj1 == sj2) { sj1 = rands(); sj2 = rands(); } else break; } a[sj1] = shu(); a[sj2] = shu(); for (j = 0; j < 16; j++) b[j] = a[j]; flag = 0; score = 0; break; } /* 继续游戏 */ else if (t == 2) { jx = 1; shua(); } else { shua(); ct(32, 19, 15, 3, 207); printf(" 游戏已退出!"); textcolor(255); gotoxy(0, 0); getch(); return 0; } } } /* 决定是否产生新的数及其位置以及游戏是否失败 */ /* flag,pd,x赋初值 */ flag = 0; pd = 1; x = 0; /* 判断移动后是否有空位:pd=0为有空位,pd=1为无空位 */ for (j = 0; j < 16; j++) { if (a[j] == 0) pd = 0; } /* 移动是否有效:x=1有效,x=0无效 */ for (j = 0; j < 16; j++) { if (a[j] != b[j]) { x = 1; break; } } /* 移动有效时且有空位时产生新数 */ if (pd == 0 && x == 1) { for (;;) { sj = rands(); if (a[sj] == 0) { a[sj] = shu(); break; } else; } } /* 游戏是否失败的判定 */ /* 移动后无空位 */ else if (pd != 0) { /* 判断是否还有可合并的项 */ for (j = 0; j < 16; j++) { if (j != 3 && j != 7 && j != 11 && j != 15 && a[j] == a[j + 1]) { flag = flag + 1; } if (j != 12 && j != 13 && j != 14 && j != 15 && a[j] == a[j + 4]) { flag = flag + 1; } if (j != 0 && j != 4 && j != 8 && j != 12 && a[j] == a[j - 1]) { flag = flag + 1; } if (j != 0 && j != 1 && j != 2 && j != 3 && a[j] == a[j - 4]) { flag = flag + 1; } } /* 无空位且没有可合并的项时游戏失败 */ if (flag == 0) { /* 失败时输出游戏结果 */ /*显示*/ prin(a, &score, &scoremax); shua(); ct(20, 19, 15, 3, 271); printf(" 游戏结束!"); textcolor(240); gotoxy(38, 19); printf("Whether continue the game ?"); gotoxy(44, 21); printf("@Yes No"); gotoxy(44, 21); t = 1; an = 75; yx = 0; for (;;) { an = getch(); switch (an) { case 75: if (t == 1) { t = 0; gotoxy(53, 21); printf("@"); gotoxy(44, 21); printf(" "); } else { t = 1; gotoxy(44, 21); printf("@"); gotoxy(53, 21); printf(" "); } break; case 77: if (t == 1) { t = 0; gotoxy(53, 21); printf("@"); gotoxy(44, 21); printf(" "); } else { t = 1; gotoxy(44, 21); printf("@"); gotoxy(53, 21); printf(" "); } break; case '\r': yx = 1; break; } if (yx == 1) { yx = 0; break; } } shua(); textcolor(255); gotoxy(0, 0); if (t == 1) { for (j = 0; j < 16; j++) a[j] = 0; sj = rands(); sj1 = rands(); sj2 = rands(); for (;;) { if (sj1 == sj2 || sj1 == sj || sj2 == sj) { sj1 = rands(); sj2 = rands(); } else break; } a[sj] = shu(); a[sj1] = shu(); a[sj2] = shu(); score = 0; jx = 0; } else { shua(); ct(32, 19, 15, 3, 207); printf(" 游戏已退出!"); textcolor(255); gotoxy(0, 0); getch(); return 0; } } } else; /* 显示结果 */ prin(a, &score, &scoremax); } return 0; } /*显示函数*/ void p(int*a, int i, int x, int y) { int x1 = x, y1 = y; textcolor(col(a + i)); x1 = x + (i % 4) * 7; y1 = y + (i / 4) * 4; gotoxy(x1, y1); printf(" "); gotoxy(x1, y1 + 1); kg(a + i); gotoxy(x1, y1 + 2); printf(" "); textcolor(240); } /*字体空格声明*/ void kg(int*a) { if (*a == 0) printf(" ", *a); else if (*a<10) printf(" %d ", *a); else if (*a<100) printf(" %d ", *a); else if (*a<1000) printf(" %d ", *a); else if (*a<10000) printf("%d ", *a); else printf("%d", *a); } /*单个颜色返回值*/ int col(int*a) { int co = 127; if (*(a) == 0) co = 127; else if (*(a) == 2) co = 143; else if (*(a) == 4) co = 191; else if (*(a) == 8) co = 175; else if (*(a) == 16) co = 239; else if (*(a) == 32) co = 223; else if (*(a) == 64) co = 207; else if (*(a) == 128) co = 95; else if (*(a) == 256) co = 159; else if (*(a) == 512) co = 63; else if (*(a) == 1024) co = 111; else if (*(a) == 2048) co = 79; else if (*(a) == 4096) co = 287; else co = 271; return co; } /*标题字幕2048*/ void p2048(int x, int y) { int i; textcolor(240); gotoxy(0, 0); for (i = 0; i <= 2000; i++) { printf(" "); } //输出数字2 textcolor(207); gotoxy(x, y); printf(" "); gotoxy(x, y + 1); printf(" "); gotoxy(x + 7, y + 2); printf(" "); gotoxy(x + 7, y + 3); printf(" "); gotoxy(x, y + 4); printf(" "); gotoxy(x, y + 5); printf(" "); gotoxy(x, y + 6); printf(" "); gotoxy(x, y + 7); printf(" "); gotoxy(x, y + 8); printf(" "); gotoxy(x, y + 9); printf(" "); //输出数字0 textcolor(239); gotoxy(x + 13, y); printf(" "); gotoxy(x + 13, y + 1); printf(" "); gotoxy(x + 13, y + 2); printf(" "); gotoxy(x + 13 + 7, y + 2); printf(" "); gotoxy(x + 13, y + 3); printf(" "); gotoxy(x + 13 + 7, y + 3); printf(" "); gotoxy(x + 13, y + 4); printf(" "); gotoxy(x + 13 + 7, y + 4); printf(" "); gotoxy(x + 13, y + 5); printf(" "); gotoxy(x + 13 + 7, y + 5); printf(" "); gotoxy(x + 13, y + 6); printf(" "); gotoxy(x + 13 + 7, y + 6); printf(" "); gotoxy(x + 13, y + 7); printf(" "); gotoxy(x + 13 + 7, y + 7); printf(" "); gotoxy(x + 13, y + 8); printf(" "); gotoxy(x + 13, y + 9); printf(" "); //输出数字4 textcolor(159); gotoxy(x + 26, y); printf(" "); gotoxy(x + 26 + 7, y); printf(" "); gotoxy(x + 26, y + 1); printf(" "); gotoxy(x + 26 + 7, y + 1); printf(" "); gotoxy(x + 26, y + 2); printf(" "); gotoxy(x + 26 + 7, y + 2); printf(" "); gotoxy(x + 26, y + 3); printf(" "); gotoxy(x + 26 + 7, y + 3); printf(" "); gotoxy(x + 26, y + 4); printf(" "); gotoxy(x + 26, y + 5); printf(" "); gotoxy(x + 26, y + 6); printf(" "); gotoxy(x + 26, y + 7); printf(" "); gotoxy(x + 26, y + 8); printf(" "); gotoxy(x + 26, y + 9); printf(" "); //输出数字8 textcolor(175); for (i = 0; i<10; i++) { gotoxy(x + 39, y + i); printf(" "); gotoxy(x + 44, y + i); printf(" "); } textcolor(240); gotoxy(x + 42, y + 2); printf(" "); gotoxy(x + 42, y + 3); printf(" "); gotoxy(x + 42, y + 6); printf(" "); gotoxy(x + 42, y + 7); printf(" "); //长条 ct(35, 15, 20, 1, 240); printf(" Game Sart"); ct(35, 18, 20, 1, 240); printf("How to play"); printf("\n"); printf("\n\n\n Made by 柳猫"); printf("\n\n\n 更多干货资料请加群710520381"); gotoxy(35 - 25, 15); textcolor(255); gotoxy(0, 0); } /*画长条函数*/ void ct(int x, int y, int l, int k, int c) { int i = 0, j = 0; textcolor(c); gotoxy(x, y); for (i = 0; i<k; i++) { gotoxy(x, y + i); for (j = 0; j<l; j++) { printf(" "); } } gotoxy(x + 1, y + k / 2); } void shua(void) { gotoxy(0, 18); textcolor(255); printf(" \n"); printf(" \n"); printf(" \n"); printf(" \n"); printf(" \n"); printf(" \n"); gotoxy(0, 0); } /*胜利画面测试函数*/ int test(void) { int i, t, yx; char an; textcolor(240); gotoxy(0, 0); for (i = 0; i <= 500; i++) { printf(" "); } gotoxy(0, 0); HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cci; GetConsoleCursorInfo(hOut, &cci); cci.bVisible = FALSE; SetConsoleCursorInfo(hOut, &cci); //printf("\n游戏胜利!t\n重新开始游戏请按1\n继续游戏请按2\n退出请按其他键3\n"); 2 1 3 /* 2 继续选中 textcolor(240); gotoxy(36,19); printf(" "); textcolor(159); gotoxy(38,19); printf(" Continue "); 2 继续平常 textcolor(240); gotoxy(36,19); printf(" "); textcolor(159); gotoxy(49,19); printf(" Continue "); 1 重新选中 textcolor(240); gotoxy(36,20); printf(" "); textcolor(239); gotoxy(38,20); printf(" Restart "); 1 重新平常 textcolor(240); gotoxy(36,20); printf(" "); textcolor(239); gotoxy(49,20); printf(" Restart "); 3 退出选中 textcolor(240); gotoxy(36,21); printf(" "); textcolor(175); gotoxy(38,21); printf(" Exit "); 3 退出平常 textcolor(240); gotoxy(36,21); printf(" "); textcolor(175); gotoxy(49,21); printf(" Exit "); // 0000000000000000000000000 24 */ shua(); ct(20, 19, 15, 3, 207); printf(" 游戏胜利!"); textcolor(159); gotoxy(38, 19); printf(" Continue "); textcolor(239); gotoxy(49, 20); printf(" Restart "); textcolor(175); gotoxy(49, 21); printf(" Exit "); t = 2; an = 75; yx = 0; for (;;) { an = getch(); switch (an) { case 72: if (t == 2) { t = 3; textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(38, 21); printf(" Exit "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(47, 19); printf(" Continue "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(49, 20); printf(" Restart "); } else if (t == 1) { t = 2; textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(38, 19); printf(" Continue "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(47, 20); printf(" Restart "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(49, 21); printf(" Exit "); } else { t = 1; textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(38, 20); printf(" Restart "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(47, 21); printf(" Exit "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(49, 19); printf(" Continue "); } break; case 80: if (t == 2) { t = 1; textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(38, 20); printf(" Restart "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(47, 19); printf(" Continue "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(49, 21); printf(" Exit "); } else if (t == 1) { t = 3; textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(38, 21); printf(" Exit "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(47, 20); printf(" Restart "); textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(49, 19); printf(" Continue "); } else { t = 2; textcolor(240); gotoxy(36, 19); printf(" "); textcolor(159); gotoxy(38, 19); printf(" Continue "); textcolor(240); gotoxy(36, 21); printf(" "); textcolor(175); gotoxy(47, 21); printf(" Exit "); textcolor(240); gotoxy(36, 20); printf(" "); textcolor(239); gotoxy(49, 20); printf(" Restart "); } break; case '\r': yx = 1; break; } if (yx == 1) { yx = 0; break; } } shua(); textcolor(255); gotoxy(0, 0); textcolor(240); if (t == 2) printf("Continue "); else if (t == 1) printf("Restart "); else printf("Exit "); return 0; } 附上效果图: 游戏开始界面: 游戏进行中: 游戏结束:(更多干货和资料请直接联系我,也可以加群710520381,邀请码:柳猫,欢迎大家共同讨论)

优秀的个人博客,低调大师

JavaScript小游戏,键盘英雄!

该游戏仅供探讨交流。 该游戏可随意传播,请保留声明与出处。 几年前的一个夜晚孤枕难眠,突然来了灵感。创作游戏的初衷是巩固js基础,有朋友问我,为什么取这个名字呢? 可以想象你是电影的主角,打字的速度已达到独孤求败的境界,狂风呼啸电闪雷鸣,键盘声响起,键盘在你指尖灵动肆意纷飞,眨眼的功夫一部20万字撰写完毕。 生活不止眼前的苟且,还有诗和代码!当然这也是需要有想象力的。 欢迎有更多想象力的朋友分享思维,在这款游戏上面继续扩展。 html部分可以用js来生成,其中加入了H5中的audio元素,按键音效,后续还可以增加背景音乐,或者再来一个升级音效,一直没有增加的原因是,没有找到满意的音乐,看来想做一个全能大神还是有难度的,哈哈!css建议用BEM或OOCSS设计模式便于维护和模块化功能扩展。js注释的很明白,还可以添加很多功能,得分也可以更加复杂,可以生成随机数0~99,或增加生成黄色按键几率10%得分10,或增加生成蓝色按键几率5%得分30,增加趣味性,只需要调整升级机制。越来越有趣了,有时候开发要保持童心不是么? 完整项目地址:https://github.com/af66666/typing_hero.git 上菜: html↓ <div id="pg"> <div> <div id="esc">Esc</div> </div> <div id="key"> <div class="white">q</div> <div class="white">w</div> <div class="white">e</div> <div class="white">r</div> <div class="white">t</div> <div class="white">y</div> <div class="white">u</div> <div class="white">i</div> <div class="white">o</div> <div class="white">p</div> <div class="white">[</div> <div class="white">]</div> <div class="white">a</div> <div class="white">s</div> <div class="white">d</div> <div class="white">f</div> <div class="white">g</div> <div class="white">h</div> <div class="white">j</div> <div class="white">k</div> <div class="white">l</div> <div class="white">;</div> <div class="white">'</div> <div class="white">z</div> <div class="white">x</div> <div class="white">c</div> <div class="white">v</div> <div class="white">b</div> <div class="white">n</div> <div class="white">m</div> <div class="white">,</div> <div class="white">.</div> <div class="white">/</div> </div> <p id="score">-</p> <p id="level">-</p> <i id="go"></i> </div> <!--<audio id="music" src="18839369087.mp3"></audio>--> css↓ #pg { width: 800px; height: 600px; margin: 0 auto; position: relative; background-image:linear-gradient(60deg,#62C292 0%,#F8CBAD 25%,#62C292 50%,#F8CBAD 75%,#62C292 100%); } #pg:before { content: ""; display: table; } #pg>div { margin: 20px; overflow: hidden; } #pg>div+div { margin: 80px 0; padding: 20px 0; } #pg div div { float: left; width: 40px; height: 40px; border: 5px solid #000; border-radius: 5px; text-align: center; line-height: 40px; font-size: 22px; font-family: helvetica; font-weight: bold; margin-right: 2px; margin-bottom: 2px; cursor: pointer; } #key div:first-child { margin-left: 88px; } #key div:nth-child(13) { margin-left: 108px; } #key div:nth-child(24) { margin-left: 128px; } #pg p { position: absolute; top: 390px; left: 280px; font-family: helvetica; font-size: 36px; font-weight: bold; color: #fff; opacity: 0.5; cursor: pointer; } #pg p+p { top: 430px; } .red { background: #f00; opacity: 0.5; color: #fff; } #go { display: none; width: 800px; height: 600px; position: absolute; top: 0; left: 0; text-align: center; line-height: 600px; font-size: 160px; font-weight: bold; font-family: helvetica; color: red; cursor: pointer; transition: font-size 0.5s ease-out; } #go:hover { font-size: 200px; } js↓ var destroy = { state: 0, //游戏状态 timer: null, //游戏引擎,主定时器 level: 1, //等级 sc: 0, //积分 many: 0, //消除的个数 MANYS: 20, //升级需要消除的个数 interval: 500, //红色生成的速度 LINTERVAL: 50, //红色生成速度加快 MIN: 100, //生成红色的最快速度 READY: 0, //就绪状态 RUNNING: 1, //正在游戏中 PAUSE: 2, //暂停状态 GAMEOVER: 3, //游戏结束 //就绪状态 ready: function() { this.state = this.READY; //就绪状态 this.sc = 0; //游戏初始化 this.many = 0; //游戏初始化 this.level = 1; //游戏初始化 this.interval = 500; //游戏初始化 esc.style.color = '#00ffff'; go.innerHTML = 'ready'; go.style.background = '#f7f7f7'; go.style.display = 'block'; go.onclick = function() { go.innerHTML = ' '; go.style.display = 'none'; destroy.start(); //destroy.playAudio(); } score.innerHTML = 'SCORE:' + this.sc; level.innerHTML = 'LEVEL:' + this.level; }, //游戏进行中 start: function() { this.state = this.RUNNING; var ds = key.querySelectorAll('div'); this.timer = setInterval(function() { var white = key.querySelectorAll('div.white'); var tmp = white.length; var n = parseInt(Math.random() * tmp); if(tmp > 0){ white[n].className = "red"; }else{ destroy.gameOver(); } for(var r = 0; r < ds.length; r++) { ds[r].style.transform = ''; } }, this.interval); esc.onclick = function() { destroy.pause(); //destroy.playAudio(); } document.onkeydown = function(e) { switch(e.keyCode) { case 27: esc.style.color = '#f00'; destroy.pause(); //destroy.playAudio(); break; case 219: if(ds[10].className == 'red') { ds[10].className = 'white'; ds[10].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; case 221: if(ds[11].className == 'red') { ds[11].className = 'white'; ds[11].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; case 186: if(ds[21].className == 'red') { ds[21].className = 'white'; ds[21].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; case 222: if(ds[22].className == 'red') { ds[22].className = 'white'; ds[22].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; case 188: if(ds[30].className == 'red') { ds[30].className = 'white'; ds[30].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; case 190: if(ds[31].className == 'red') { ds[31].className = 'white'; ds[31].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; case 191: if(ds[32].className == 'red') { ds[32].className = 'white'; ds[32].style.transform = 'rotate(30deg)'; destroy.createLevel(); //destroy.playAudio(); } break; } for(var i = 0; i < ds.length; i++) { if(String.fromCharCode(e.which).toLowerCase() == ds[i].innerHTML) { if(ds[i].className == 'red') { ds[i].className = 'white'; ds[i].style.transform = 'scale(1.8)'; destroy.createLevel(); //destroy.playAudio(); } } } var num = e.keyCode; //检测 var en = String.fromCharCode(e.which); //检测 console.log(num); //检测 console.log(String(en)); //检测 } }, //暂停状态 pause: function() { this.state = this.PAUSE; //暂停状态 go.innerHTML = 'pause'; go.style.opacity = 0.5; go.style.display = 'block'; go.style.background = '#fff'; clearInterval(this.timer); document.onkeydown = function(e) { switch(e.keyCode) { case 27: esc.style.color = '#00ffff'; go.style.display = 'none'; destroy.start(); //destroy.playAudio(); } } }, //游戏结束 gameOver: function() { this.state = this.GAMEOVER; clearInterval(this.timer); go.style.opacity = 1; go.innerHTML = 'OVER'; go.style.display = 'block'; go.style.background = '#000'; this.sc = 0; //游戏初始化 this.level = 1; //游戏初始化 this.interval = 500; //游戏初始化 this.many = 0; //游戏初始化 document.onkeydown = function(e) { switch(e.keyCode) { case 83: go.style.display = 'none'; var ds = key.querySelectorAll('div'); for(var i = 0; i < ds.length; i++) { ds[i].className = 'white'; } score.innerHTML = 'SCORE:' + 0; level.innerHTML = 'LEVEL:' + 1; destroy.start(); //destroy.playAudio(); } }; go.onclick = function(){ go.style.display = 'none'; var ds = key.querySelectorAll('div'); for(var i = 0; i < ds.length; i++) { ds[i].className = 'white'; } score.innerHTML = 'SCORE:' + 0; level.innerHTML = 'LEVEL:' + 1; destroy.start(); //destroy.playAudio(); }; }, //等级得分机制 createLevel: function() { this.sc += 5; this.many++; if(this.many == this.MANYS) { this.many = 0; this.level++; if(this.interval >= this.MIN) { this.interval -= this.LINTERVAL; clearInterval(this.timer); //清除定时器 destroy.start(); //重新启动定时器 } } score.innerHTML = 'SCORE:' + this.sc; level.innerHTML = 'LEVEL:' + this.level; if(this.level >= 10) { level.innerHTML = '您已打破世界纪录'; } }, //音频 playAudio: function() { music.pause(); music.load(); music.play(); }, } //运行 window.onload = function() { destroy.ready(); } html部分可以用js来生成,其中加入了H5中的audio元素,按键音效,后续还可以增加背景音乐,或者再来一个升级音效,一直没有增加的原因是,没有找到满意的音乐,看来想做一个全能大神还是有难度的,哈哈!css建议用BEM或OOCSS设计模式便于维护和模块化扩展。js注释的很明白,还可以添加很多功能,得分也可以更加复杂,可以生成随机数0~99,或增加生成黄色按键几率10%得分10,或增加生成蓝色按键几率5%得分30,增加趣味性,只需要调整升级机制。越来越有趣了,有时候开发要保持童心不是么? 后续将继续改进扩展,欢迎指导交流!

优秀的个人博客,低调大师

Java实现简易联网坦克对战小游戏

目录 介绍 本项目的Github地址 基础版本 客户端连接上服务器 定义应用层协议 TankNewMsg TankMoveMsg MissileNewMsg TankDeadMsg和MissileDeadMsg 游戏的原理, 图形界面(非重点) 游戏逻辑 网络联机 改进版本. 定义更精细的协议 坦克战亡后服务器端的处理 客户端线程同步 添加图片 总结与致谢 介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简单的应用层协议来让自己应用进行网络通信. 本项目的Github地址 基础版本 游戏的原理, 图形界面(非重点) 多张图片快速连续地播放, 图片中的东西就能动起来形成视频, 对视频中动起来的东西进行操作就变成游戏了. 在一个坦克对战游戏中, 改变一辆坦克每一帧的位置, 当多帧连续播放的时候, 视觉上就有了控制坦克的感觉. 同理, 改变子弹每一帧的位置, 看起来就像是发射了一发炮弹. 当子弹和坦克的位置重合, 也就是两个图形的边界相碰时, 在碰撞的位置放上一个爆炸的图片, 就完成了子弹击中坦克发生爆炸的效果. 在本项目借助坦克游戏认识网络知识和面向对象思想, 游戏的显示与交互使用到了Java中的图形组件, 如今Java已较少用于图形交互程序开发, 本项目也只是使用了一些简单的图形组件. 在本项目中, 游戏的客户端由TankClient类控制, 游戏的运行和所有的图形操作都包含在这个类中, 下面会介绍一些主要的方法. //类TankClient,继承自Frame类 //继承Frame类后所重写的两个方法paint()和update() //在paint()方法中设置在一张图片中需要画出什么东西. @Override publicvoidpaint(Graphicsg){ //下面三行画出游戏窗口左上角的游戏参数 g.drawString("missilescount:"+missiles.size(),10,50); g.drawString("explodescount:"+explodes.size(),10,70); g.drawString("tankscount:"+tanks.size(),10,90); //检测我的坦克是否被子弹打到,并画出子弹 for(inti=0;i<missiles.size();i++){ Missilem=missiles.get(i); if(m.hitTank(myTank)){ TankDeadMsgmsg=newTankDeadMsg(myTank.id); nc.send(msg); MissileDeadMsgmmsg=newMissileDeadMsg(m.getTankId(),m.getId()); nc.send(mmsg); } m.draw(g); } //画出爆炸 for(inti=0;i<explodes.size();i++){ Explodee=explodes.get(i); e.draw(g); } //画出其他坦克 for(inti=0;i<tanks.size();i++){ Tankt=tanks.get(i); t.draw(g); } //画出我的坦克 myTank.draw(g); } /* *update()方法用于写每帧更新时的逻辑. *每一帧更新的时候,我们会把该帧的图片画到屏幕中. *但是这样做是有缺陷的,因为把一副图片画到屏幕上会有延时,游戏显示不够流畅 *所以这里用到了一种缓冲技术. *先把图像画到一块幕布上,每帧更新的时候直接把画布推到窗口中显示 */ @Override publicvoidupdate(Graphicsg){ if(offScreenImage==null){ offScreenImage=this.createImage(800,600);//创建一张画布 } GraphicsgOffScreen=offScreenImage.getGraphics(); Colorc=gOffScreen.getColor(); gOffScreen.setColor(Color.GREEN); gOffScreen.fillRect(0,0,GAME_WIDTH,GAME_HEIGHT); gOffScreen.setColor(c); paint(gOffScreen);//先在画布上画好 g.drawImage(offScreenImage,0,0,null);//直接把画布推到窗口 } //这是加载游戏窗口的方法 publicvoidlaunchFrame(){ this.setLocation(400,300);//设置游戏窗口相对于屏幕的位置 this.setSize(GAME_WIDTH,GAME_HEIGHT);//设置游戏窗口的大小 this.setTitle("TankWar");//设置标题 this.addWindowListener(newWindowAdapter(){//为窗口的关闭按钮添加监听 @Override publicvoidwindowClosing(WindowEvente){ System.exit(0); } }); this.setResizable(false);//设置游戏窗口的大小不可改变 this.setBackground(Color.GREEN);//设置背景颜色 this.addKeyListener(newKeyMonitor());//添加键盘监听, this.setVisible(true);//设置窗口可视化,也就是显示出来 newThread(newPaintThread()).start();//开启线程,把图片画出到窗口中 dialog.setVisible(true);//显示设置服务器IP,端口号,自己UDP端口号的对话窗口 } //在窗口中画出图像的线程,定义为每50毫秒画一次. classPaintThreadimplementsRunnable{ publicvoidrun(){ while(true){ repaint(); try{ Thread.sleep(50); }catch(InterruptedExceptione){ e.printStackTrace(); } } } } 以上就是整个游戏图形交互的主要部分, 保证了游戏能正常显示后, 下面我们将关注于游戏的逻辑部分. 游戏逻辑 在游戏的逻辑中有两个重点, 一个是坦克, 另一个是子弹. 根据面向对象的思想, 分别把这两者封装成两个类, 它们所具有的行为都在类对应有相应的方法. 坦克的字段 publicintid;//作为网络中的标识 publicstaticfinalintXSPEED=5;//左右方向上每帧移动的距离 publicstaticfinalintYSPEED=5;//上下方向每帧移动的距离 publicstaticfinalintWIDTH=30;//坦克图形的宽 publicstaticfinalintHEIGHT=30;//坦克图形的高 privatebooleangood;//根据true和false把坦克分成两类,游戏中两派对战 privateintx,y;//坦克的坐标 privatebooleanlive=true;//坦克是否活着,死了将不再画出 privateTankClienttc;//客户端类的引用 privatebooleanbL,bU,bR,bD;//用于判断键盘按下的方向 privateDirdir=Dir.STOP;//坦克的方向 privateDirptDir=Dir.D;//炮筒的方向 由于在TankClient类中的paint方法中需要画出图形, 根据面向对象的思想, 要画出一辆坦克, 应该由坦克调用自己的方法画出自己. publicvoiddraw(Graphicsg){ if(!live){ if(!good){ tc.getTanks().remove(this);//如果坦克死了就把它从容器中去除,并直接结束 } return; } //画出坦克 Colorc=g.getColor(); if(good)g.setColor(Color.RED); elseg.setColor(Color.BLUE); g.fillOval(x,y,WIDTH,HEIGHT); g.setColor(c); //画出炮筒 switch(ptDir){ caseL: g.drawLine(x+WIDTH/2,y+HEIGHT/2,x,y+HEIGHT/2); break; caseLU: g.drawLine(x+WIDTH/2,y+HEIGHT/2,x,y); break; caseU: g.drawLine(x+WIDTH/2,y+HEIGHT/2,x+WIDTH/2,y); break; //...省略部分方向 } move();//每次画完改变坦克的坐标,连续画的时候坦克就动起来了 } 上面提到了改变坦克坐标的move()方法, 具体代码如下: privatevoidmove(){ switch(dir){//根据坦克的方向改变坐标 caseL://左 x-=XSPEED; break; caseLU://左上 x-=XSPEED; y-=YSPEED; break; //...省略 } if(dir!=Dir.STOP){ ptDir=dir; } //防止坦克走出游戏窗口,越界时要停住 if(x<0)x=0; if(y<30)y=30; if(x+WIDTH>TankClient.GAME_WIDTH)x=TankClient.GAME_WIDTH-WIDTH; if(y+HEIGHT>TankClient.GAME_HEIGHT)y=TankClient.GAME_HEIGHT-HEIGHT; } 上面提到了根据坦克的方向改变坦克的左边, 而坦克的方向通过键盘改变. 代码如下: publicvoidkeyPressed(KeyEvente){//接收接盘事件 intkey=e.getKeyCode(); //根据键盘按下的按键修改bL,bU,bR,bD四个布尔值,回后会根据四个布尔值判断上,左上,左等八个方向 switch(key){ caseKeyEvent.VK_A://按下键盘A键,意味着往左 bL=true; break; caseKeyEvent.VK_W://按下键盘W键,意味着往上 bU=true; break; caseKeyEvent.VK_D: bR=true; break; caseKeyEvent.VK_S: bD=true; break; } locateDirection();//根据四个布尔值判断八个方向的方法 } privatevoidlocateDirection(){ DiroldDir=this.dir;//记录下原来的方法,用于联网 //根据四个方向的布尔值判断八个更细分的方向 //比如左和下都是true,证明玩家按的是左下,方向就该为左下 if(bL&&!bU&&!bR&&!bD)dir=Dir.L; elseif(bL&&bU&&!bR&&!bD)dir=Dir.LU; elseif(!bL&&bU&&!bR&&!bD)dir=Dir.U; elseif(!bL&&bU&&bR&&!bD)dir=Dir.RU; elseif(!bL&&!bU&&bR&&!bD)dir=Dir.R; elseif(!bL&&!bU&&bR&&bD)dir=Dir.RD; elseif(!bL&&!bU&&!bR&&bD)dir=Dir.D; elseif(bL&&!bU&&!bR&&bD)dir=Dir.LD; elseif(!bL&&!bU&&!bR&&!bD)dir=Dir.STOP; //可以先跳过这段代码,用于网络中其他客户端的坦克移动 if(dir!=oldDir){ TankMoveMsgmsg=newTankMoveMsg(id,x,y,dir,ptDir); tc.getNc().send(msg); } } //对键盘释放的监听 publicvoidkeyReleased(KeyEvente){ intkey=e.getKeyCode(); switch(key){ caseKeyEvent.VK_J://设定J键开火,当释放J键时发出一发子弹 fire(); break; caseKeyEvent.VK_A: bL=false; break; caseKeyEvent.VK_W: bU=false; break; caseKeyEvent.VK_D: bR=false; break; caseKeyEvent.VK_S: bD=false; break; } locateDirection(); } 上面提到了坦克开火的方法, 这也是坦克最后一个重要的方法了, 代码如下, 后面将根据这个方法引出子弹类. privateMissilefire(){ if(!live)returnnull;//如果坦克死了就不能开火 intx=this.x+WIDTH/2-Missile.WIDTH/2;//设定子弹的x坐标 inty=this.y+HEIGHT/2-Missile.HEIGHT/2;//设定子弹的y坐标 Missilem=newMissile(id,x,y,this.good,this.ptDir,this.tc);//创建一颗子弹 tc.getMissiles().add(m);//把子弹添加到容器中. //网络部分可暂时跳过,发出一发子弹后要发送给服务器并转发给其他客户端. MissileNewMsgmsg=newMissileNewMsg(m); tc.getNc().send(msg); returnm; } 子弹类, 首先是子弹的字段 publicstaticfinalintXSPEED=10;//子弹每帧中坐标改变的大小,比坦克大些,子弹当然要飞快点嘛 publicstaticfinalintYSPEED=10; publicstaticfinalintWIDTH=10; publicstaticfinalintHEIGHT=10; privatestaticintID=10; privateintid;//用于在网络中标识的id privateTankClienttc;//客户端的引用 privateinttankId;//表明是哪个坦克发出的 privateintx,y;//子弹的坐标 privateDirdir=Dir.R;//子弹的方向 privatebooleanlive=true;//子弹是否存活 privatebooleangood;//子弹所属阵营,我方坦克自能被地方坦克击毙 子弹类中同样有draw(), move()等方法, 在此不重复叙述了, 重点关注子弹打中坦克的方法. 子弹是否打中坦克, 是调用子弹自身的判断方法判断的. publicbooleanhitTank(Tankt){ //如果子弹是活的,被打中的坦克也是活的 //子弹和坦克不属于同一方 //子弹的图形碰撞到了坦克的图形 //认为子弹打中了坦克 if(this.live&&t.isLive()&&this.good!=t.isGood()&&this.getRect().intersects(t.getRect())){ this.live=false;//子弹生命设置为false t.setLive(false);//坦克生命设置为false tc.getExplodes().add(newExplode(x,y,tc));//产生一个爆炸,坐标为子弹的坐标 returntrue; } returnfalse; } 补充, 坦克和子弹都以图形的方式显示, 在本游戏中通过Java的原生api获得图形的矩形框并判断是否重合(碰撞) publicRectanglegetRect(){ returnnewRectangle(x,y,WIDTH,HEIGHT); } 在了解游戏中两个主要对象后, 下面介绍整个游戏的逻辑. 加载游戏窗口后, 客户端会创建一个我的坦克对象, 初始化三个容器, 它们分别用于存放其他坦克, 子弹和爆炸. 当按下开火键后, 会创建一个子弹对象, 并加入到子弹容器中(主战坦克发出一棵炮弹), 如果子弹没有击中坦克, 穿出游戏窗口边界后判定子弹死亡, 从容器中移除; 如果子弹击中了敌方坦克, 敌方坦克死亡从容器移出, 子弹也死亡从容器移出, 同时会创建一个爆炸对象放到容器中, 等爆炸的图片轮播完, 爆炸移出容器. 以上就是整个坦克游戏的逻辑. 下面将介绍重头戏, 网络联机. 网络联机 客户端连接上服务器 首先客户端通过TCP连接上服务器, 并把自己的UDP端口号发送给服务器, 这里省略描述TCP连接机制, 但是明白了连接机制后对为什么需要填写服务器端口号和IP会有更深的理解, 它们均为TCP报文段中必填的字段. 服务器通过TCP和客户端连上后收到客户端的UDP端口号信息, 并将客户端的IP地址和UDP端口号封装成一个Client对象, 保存在容器中. 这里补充一点, 为什么能获取客户端的IP地址? 因为服务器收到链路层帧后会提取出网络层数据报, 源地址的IP地址在IP数据报的首部字段中, Java对这一提取过程进行了封装, 所以我们能够直接在Java的api中获取源地址的IP. 服务器封装完Client对象后, 为客户端的主机坦克分配一个id号, 这个id号将用于往后游戏的网络传输中标识这台坦克. 同时服务器也会把自己的UDP端口号发送客户端, 因为服务器自身会开启一条UDP线程, 用于接收转发UDP包. 具体作用在后面会讲到. 客户端收到坦克id后设置到自己的主战坦克的id字段中. 并保存服务器的UDP端口号. 这里你可能会对UDP端口号产生疑问, 别急, 后面一小节将描述它的作用. 附上这部分的代码片段: //客户端 publicvoidconnect(Stringip,intport){ serverIP=ip; Sockets=null; try{ ds=newDatagramSocket(UDP_PORT);//创建UDP套接字 s=newSocket(ip,port);//创建TCP套接字 DataOutputStreamdos=newDataOutputStream(s.getOutputStream()); dos.writeInt(UDP_PORT);//向服务器发送自己的UDP端口号 DataInputStreamdis=newDataInputStream(s.getInputStream()); intid=dis.readInt();//获得服务器分配给自己坦克的id号 this.serverUDPPort=dis.readInt();//获得服务器的UDP端口号 tc.getMyTank().id=id; tc.getMyTank().setGood((id&1)==0?true:false);//根据坦克的id号的奇偶性设置坦克的阵营 }catch(IOExceptione){ e.printStackTrace(); }finally{ try{ if(s!=null)s.close();//信息交换完毕后客户端的TCP套接字关闭 }catch(IOExceptione){ e.printStackTrace(); } } TankNewMsgmsg=newTankNewMsg(tc.getMyTank()); send(msg);//发送坦克出生的消息(后面介绍) newThread(newUDPThread()).start();//开启UDP线程 } //服务器 publicvoidstart(){ newThread(newUDPThread()).start();//开启UDP线程 ServerSocketss=null; try{ ss=newServerSocket(TCP_PORT);//创建TCP欢迎套接字 }catch(IOExceptione){ e.printStackTrace(); } while(true){//监听每个客户端的连接 Sockets=null; try{ s=ss.accept();//为客户端分配一个专属TCP套接字 DataInputStreamdis=newDataInputStream(s.getInputStream()); intUDP_PORT=dis.readInt();//获得客户端的UDP端口号 Clientclient=newClient(s.getInetAddress().getHostAddress(),UDP_PORT);//把客户端的IP地址和UDP端口号封装成Client对象,以备后面使用 clients.add(client);//装入容器中 DataOutputStreamdos=newDataOutputStream(s.getOutputStream()); dos.writeInt(ID++);//给客户端的主战坦克分配一个id号 dos.writeInt(UDP_PORT); }catch(IOExceptione){ e.printStackTrace(); }finally{ try{ if(s!=null)s.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } 定义应用层协议 客户机连上服务器后, 两边分别获取了初始信息, 且客户端和服务器均开启了UDP线程. 客户端通过保存的服务器UDP端口号可以向服务器的UDP套接字发送UDP包, 服务器保存了所有连上它的Client客户端信息, 它可以向所有客户端的UDP端口发送UDP包. 此后, 整个坦克游戏的网络模型已经构建完毕, 游戏中的网络传输道路已经铺设好, 但想要在游戏中进行网络传输还差一样东西, 它就是这个网络游戏的应用层通信协议. 在本项目中, 应用层协议很简单, 只有两个字段, 一个是消息类型, 一个是消息数据(有效载荷). 这里先列出所有的具体协议, 后面将进行逐一讲解. 消息类型 消息数据 1.TANK_NEW_MSG(坦克出生信息) 坦克id, 坦克坐标, 坦克方向, 坦克好坏 2.TANK_MOVE_MSG(坦克移动信息) 坦克id, 坦克坐标, 坦克方向, 炮筒方向 3.MISSILE_NEW_MESSAGE(子弹产生信息) 发出子弹的坦克id, 子弹id, 子弹坐标, 子弹方向 4.TANK_DEAD_MESSAGE(子弹死亡的信息) 发出子弹的坦克id, 子弹id 5.MISSILE_DEAD_MESSAGE(坦克死亡的信息) 坦克id 在描述整个应用层协议体系及具体应用前需要补充一下, 文章前面提到TankClient类用于控制整个游戏客户端, 但为了解耦, 客户端将需要进行的网络操作使用另外一个NetClient类进行封装. 回到正题, 我们把应用层协议定义为一个接口, 具体到每个消息协议有具体的实现类, 这里我们将用到多态. publicinterfaceMsg{ publicstaticfinalintTANK_NEW_MSG=1; publicstaticfinalintTANK_MOVE_MSG=2; publicstaticfinalintMISSILE_NEW_MESSAGE=3; publicstaticfinalintTANK_DEAD_MESSAGE=4; publicstaticfinalintMISSILE_DEAD_MESSAGE=5; //每个消息报文,自己将拥有发送和解析的方法,为多态的实现奠定基础. publicvoidsend(DatagramSocketds,StringIP,intUDP_Port); publicvoidparse(DataInputStreamdis); } 下面将描述多态的实现给本程序带来的好处. 在NetClient这个网络接口类中, 需要定义发送消息和接收消息的方法. 想一下, 如果我们为每个类型的消息编写发送和解析的方法, 那么程序将变得复杂冗长. 使用多态后, 每个消息实现类自己拥有发送和解析的方法, 要调用NetClient中的发送接口发送某个消息就方便多了. 下面代码可能解释的更清楚. //如果没有多态的话,NetClient中将要定义每个消息的发送方法 publicvoidsendTankNewMsg(TankNewMsgmsg){ //很长... } publicvoidsendMissileNewMsg(MissileNewMsgmsg){ //很长... } //只要有新的消息类型,后面就要接着定义... //假如使用了多态,NetClient中只需要定义一个发送方法 publicvoidsend(Msgmsg){ msg.send(ds,serverIP,serverUDPPort); } //当我们要发送某个类型的消息时,只需要 TankNewMsgmsg=newTankNewMsg(); NetClientnc=newNetClient();//实践中不需要,能拿到唯一的NetClient的引用 nc.send(msg) //在NetClient类中,解析的方法如下 privatevoidparse(DatagramPacketdp){ ByteArrayInputStreambais=newByteArrayInputStream(buf,0,dp.getLength()); DataInputStreamdis=newDataInputStream(bais); intmsgType=0; try{ msgType=dis.readInt();//先拿到消息的类型 }catch(IOExceptione){ e.printStackTrace(); } Msgmsg=null; switch(msgType){//根据消息的类型,调用具体消息的解析方法 caseMsg.TANK_NEW_MSG: msg=newTankNewMsg(tc); msg.parse(dis); break; caseMsg.TANK_MOVE_MSG: msg=newTankMoveMsg(tc); msg.parse(dis); break; caseMsg.MISSILE_NEW_MESSAGE: msg=newMissileNewMsg(tc); msg.parse(dis); break; caseMsg.TANK_DEAD_MESSAGE: msg=newTankDeadMsg(tc); msg.parse(dis); break; caseMsg.MISSILE_DEAD_MESSAGE: msg=newMissileDeadMsg(tc); msg.parse(dis); break; } } 接下来介绍每个具体的协议. TankNewMsg 首先介绍的是TankNewMsg坦克出生协议, 消息类型为1. 它包含的字段有坦克id, 坦克坐标, 坦克方向, 坦克好坏. 当我们的客户端和服务器完成TCP连接后, 客户端的UDP会向服务器的UDP发送一个TankNewMsg消息, 告诉服务器自己加入到了游戏中, 服务器会将这个消息转发到所有在服务器中注册过的客户端. 这样每个客户端都知道了有一个新的坦克加入, 它们会根据TankNewMsg中新坦克的信息创建出一个新的坦克对象, 并加入到自己的坦克容器中. 但是这里涉及到一个问题: 已经连上服务器的客户端会收到新坦克的信息并把新坦克加入到自己的游戏中, 但是新坦克的游戏中并没有其他已经存在的坦克信息. 一个较为简单的方法是旧坦克在接收到新坦克的信息后也发送一条TankNewMsg信息, 这样新坦克就能把旧坦克加入到游戏中. 下面是具体的代码. (显然这个方法不太好, 每个协议应该精细地一种操作, 留到以后进行改进) //下面是TankNewMsg中解析本消息的方法 publicvoidparse(DataInputStreamdis){ try{ intid=dis.readInt(); if(id==this.tc.getMyTank().id){ return; } intx=dis.readInt(); inty=dis.readInt(); Dirdir=Dir.values()[dis.readInt()]; booleangood=dis.readBoolean(); //接收到别人的新信息,判断别人的坦克是否已将加入到tanks集合中 booleanexist=false; for(Tankt:tc.getTanks()){ if(id==t.id){ exist=true; break; } } if(!exist){//当判断到接收的新坦克不存在已有集合才加入到集合. TankNewMsgmsg=newTankNewMsg(tc); tc.getNc().send(msg);//加入一辆新坦克后要把自己的信息也发送出去. Tankt=newTank(x,y,good,dir,tc); t.id=id; tc.getTanks().add(t); } }catch(IOExceptione){ e.printStackTrace(); } } TankMoveMsg 下面将介绍TankMoveMsg协议, 消息类型为2, 需要的数据有坦克id, 坦克坐标, 坦克方向, 炮筒方向. 每当自己坦克的方向发生改变时, 向服务器发送一个TankMoveMsg消息, 经服务器转发后, 其他客户端也能收该坦克的方向变化, 然后根据数据找到该坦克并设置方向等参数. 这样才能相互看到各自的坦克在移动. 下面是发送TankMoveMsg的地方, 也就是改变坦克方向的时候. privatevoidlocateDirection(){ DiroldDir=this.dir;//记录旧的方向 if(bL&&!bU&&!bR&&!bD)dir=Dir.L; elseif(bL&&bU&&!bR&&!bD)dir=Dir.LU; elseif(!bL&&bU&&!bR&&!bD)dir=Dir.U; elseif(!bL&&bU&&bR&&!bD)dir=Dir.RU; elseif(!bL&&!bU&&bR&&!bD)dir=Dir.R; elseif(!bL&&!bU&&bR&&bD)dir=Dir.RD; elseif(!bL&&!bU&&!bR&&bD)dir=Dir.D; elseif(bL&&!bU&&!bR&&bD)dir=Dir.LD; elseif(!bL&&!bU&&!bR&&!bD)dir=Dir.STOP; if(dir!=oldDir){//如果改变后的方向不同于旧方向也就是说方向发生了改变 TankMoveMsgmsg=newTankMoveMsg(id,x,y,dir,ptDir);//创建TankMoveMsg消息 tc.getNc().send(msg);//发送 } } MissileNewMsg 下面将介绍MissileNewMsg协议, 消息类型为3, 需要的数据有发出子弹的坦克id, 子弹id, 子弹坐标, 子弹方向. 当坦克发出一发炮弹后, 需要将炮弹的信息告诉其他客户端, 其他客户端根据子弹的信息在游戏中创建子弹对象并加入到容器中, 这样才能看见相互发出的子弹. MissileNewMsg在坦克发出一颗炮弹后生成. privateMissilefire(){ if(!live)returnnull; intx=this.x+WIDTH/2-Missile.WIDTH/2; inty=this.y+HEIGHT/2-Missile.HEIGHT/2; Missilem=newMissile(id,x,y,this.good,this.ptDir,this.tc); tc.getMissiles().add(m); MissileNewMsgmsg=newMissileNewMsg(m);//生成MissileNewMsg tc.getNc().send(msg);//发送给其他客户端 returnm; } //MissileNewMsg的解析 publicvoidparse(DataInputStreamdis){ try{ inttankId=dis.readInt(); if(tankId==tc.getMyTank().id){//如果是自己发出的子弹就跳过(已经加入到容器了) return; } intid=dis.readInt(); intx=dis.readInt(); inty=dis.readInt(); Dirdir=Dir.values()[dis.readInt()]; booleangood=dis.readBoolean(); //把收到的这颗子弹添加到子弹容器中 Missilem=newMissile(tankId,x,y,good,dir,tc); m.setId(id); tc.getMissiles().add(m); }catch(IOExceptione){ e.printStackTrace(); } } TankDeadMsg和MissileDeadMsg 下面介绍TankDeadMsg和MissileDeadMsg, 它们是一个组合, 当一台坦克被击中后, 发出TankDeadMsg信息, 同时子弹也死亡, 发出MissileDeadMsg信息. MissileDeadMsg需要数据发出子弹的坦克id, 子弹id, 而TankDeadMsg只需要坦克id一个数据. //TankClient类,paint()中的代码片段,遍历子弹容器中的每颗子弹看自己的坦克有没有被打中. for(inti=0;i<missiles.size();i++){ Missilem=missiles.get(i); if(m.hitTank(myTank)){ TankDeadMsgmsg=newTankDeadMsg(myTank.id); nc.send(msg); MissileDeadMsgmmsg=newMissileDeadMsg(m.getTankId(),m.getId()); nc.send(mmsg); } m.draw(g); } //MissileDeadMsg的解析 publicvoidparse(DataInputStreamdis){ try{ inttankId=dis.readInt(); intid=dis.readInt(); //在容器找到对应的那颗子弹,设置死亡不再画出,并产生一个爆炸. for(Missilem:tc.getMissiles()){ if(tankId==tc.getMyTank().id&&id==m.getId()){ m.setLive(false); tc.getExplodes().add(newExplode(m.getX(),m.getY(),tc)); break; } } }catch(IOExceptione){ e.printStackTrace(); } } //TankDeadMsg的解析 publicvoidparse(DataInputStreamdis){ try{ inttankId=dis.readInt(); if(tankId==this.tc.getMyTank().id){//如果是自己坦克发出的死亡消息旧跳过 return; } for(Tankt:tc.getTanks()){//否则遍历坦克容器,把死去的坦克移出容器,不再画出. if(t.id==tankId){ t.setLive(false); break; } } }catch(IOExceptione){ e.printStackTrace(); } } 到此为止, 基础版本就结束了, 基础版本已经是一个能正常游戏的版本了. 改进版本. 定义更精细的协议 当前如果有一辆坦克加入服务器后, 会向其他已存在的坦克发送TankNewMsg, 其他坦克接收到TankNewMsg会往自己的坦克容器中添加这辆新的坦克. 之前描述过存在的问题: 旧坦克能把新坦克加入到游戏中, 但是新坦克不能把旧坦克加入到游戏中, 当时使用的临时解决方案是: 旧坦克接收到TankNewMsg后判断该坦克是否已经存在自己的容器中, 如果不存在则添加进容器, 并且自己发送一个TankNewMsg, 这样新的坦克接收到旧坦克的TankNewMsg, 就能把旧坦克加入到游戏里. 但是, 我们定义的TankNewMsg是发出一个坦克出生的信息, 如果把TankNewMsg同时用于引入旧坦克, 如果以后要修改TankNewMsg就会牵涉到其他的代码, 我们应该用一个新的消息来让新坦克把旧坦克加入到游戏中. 当旧坦克接收TankNewMsg后证明有新坦克加入, 它先把新坦克加入到容器中, 再向服务器发送一个TankAlreadyExistMsg, 其他坦克检查自己的容器中是否有已经准备的坦克的信息, 如果有了就不添加, 没有则把它添加到容器中. 不得不说, 使用多态后扩展协议就变得很方便了. //修改后,TankNewMsg的解析部分如下 publicvoidparse(DataInputStreamdis){ try{ intid=dis.readInt(); if(id==this.tc.getMyTank().getId()){ return; } intx=dis.readInt(); inty=dis.readInt(); Dirdir=Dir.values()[dis.readInt()]; booleangood=dis.readBoolean(); TanknewTank=newTank(x,y,good,dir,tc); newTank.setId(id); tc.getTanks().add(newTank);//把新的坦克添加到容器中 //发出自己的信息 TankAlreadyExistMsgmsg=newTankAlreadyExistMsg(tc.getMyTank()); tc.getNc().send(msg); }catch(IOExceptione){ e.printStackTrace(); } } //TankAlreadyExist的解析部分如下 publicvoidparse(DataInputStreamdis){ try{ intid=dis.readInt(); if(id==tc.getMyTank().getId()){ return; } booleanexist=false;//判定发送TankAlreadyExist的坦克是否已经存在于游戏中 for(Tankt:tc.getTanks()){ if(id==t.getId()){ exist=true; break; } } if(!exist){//不存在则添加到游戏中 intx=dis.readInt(); inty=dis.readInt(); Dirdir=Dir.values()[dis.readInt()]; booleangood=dis.readBoolean(); TankexistTank=newTank(x,y,good,dir,tc); existTank.setId(id); tc.getTanks().add(existTank); } }catch(IOExceptione){ e.printStackTrace(); } } 坦克战亡后服务器端的处理 当一辆坦克死后, 服务器应该从Client集合中删除掉该客户端的信息, 从而不用向该客户端发送信息, 减轻负载.而且服务器应该开启一个新的UDP端口号用于接收坦克死亡的消息, 不然这个死亡的消息会转发给其他客户端. 所以在客户端进行TCP连接的时候要把这个就收坦克死亡信息的UDP端口号也发送给客户端. 被击败后, 弹框通知游戏结束. //服务端添加的代码片段 intdeadTankUDPPort=dis.readInt();//获得死亡坦克客户端的UDP端口号 for(inti=0;i<clients.size();i++){//从Client集合中删除该客户端. Clientc=clients.get(i); if(c.UDP_PORT==deadTankUDPPort){ clients.remove(c); } } //而客户端则在向其他客户端发送死亡消息后通知服务器把自己从客户端容器移除 for(inti=0;i<missiles.size();i++){ Missilem=missiles.get(i); if(m.hitTank(myTank)){ TankDeadMsgmsg=newTankDeadMsg(myTank.getId());//发送坦克死亡的消息 nc.send(msg); MissileDeadMsgmmsg=newMissileDeadMsg(m.getTankId(),m.getId());//发送子弹死亡的消息,通知产生爆炸 nc.send(mmsg); nc.sendTankDeadMsg();//告诉服务器把自己从Client集合中移除 gameOverDialog.setVisible(true);//弹窗结束游戏 } m.draw(g); } 完成这个版本后, 多人游戏时游戏性更强了, 当一个玩家死后他可以重新开启游戏再次加入战场. 但是有个小问题, 他可能会加入到击败他的坦克的阵营, 因为服务器为坦克分配的id好是递增的, 而判定坦克的阵营仅通过id的奇偶判断. 但就这个版本来说服务器端处理死亡坦克的任务算是完成了. 客户端线程同步 在完成基础版本后考虑过这个问题, 因为在游戏中, 由于延时的原因, 可能会造成各个客户端线程不同步. 处理手段可以是每隔一定时间, 各个客户端向服务器发送自己坦克的位置消息, 服务器再将该位置消息通知到其他客户端, 进行同步. 但是在本游戏中, 只要坦克的方向一发生移动就会发送一个TankMoveMsg包, TankMoveMsg消息中除了包含坦克的方向, 也包含坦克的坐标, 相当于做了客户端线程同步. 所以考虑暂时不需要再额外进行客户端同步了. 添加图片 在基础版本中, 坦克和子弹都是通过画一个圆表示, 现在添加坦克和子弹的图片为游戏注入灵魂. 总结与致谢 最后回顾整个项目, 整个项目并没有用到什么高新技术, 相反这是一个十多年前用纯Java实现的教学项目. 我觉得项目中的网络部分对我的帮助非常大. 我最近看完了《计算机网络:自顶向下方法》, 甚至把里面的课后复习题都做了一遍, 要我详细描述TCP三次握手, 如何通过DHCP协议获取IP地址, DNS的解析过程都不是问题, 但是总感觉理论与实践之间差了点东西. 现在我重新考虑协议这个名词, 在网络中, 每一种协议定义了一种端到端的数据传输规则, 从应用层到网络层, 只要有数据传输的地方就需要协议. 人类的智慧在协议中充分体现, 比如提供可靠数据传输和拥塞控制的TCP协议和轻便的UDP协议, 它们各有优点, 在各自的领域作出贡献. 但是协议最终是要执行的, 在本项目中运输层协议可以直接调用Java api实现, 但是应用层协议就要自己定义了. 尽管只是定义了几个超级简单的协议, 但是定义过的协议在发送端和接收端是如何处理的, 是落实到代码敲出来的. 当整个项目做完后, 再次考虑协议这个名词, 能看出它共通的地方, 如果让我设计一个通信协议, 我也不会因对设计协议完全没有概念而彷徨了, 当然设计得好不好就另说咯. 最后隆重致谢本项目的制作者马士兵老师, 除了简单的网络知识, 马老师在项目中不停强调程序设计的重要性, 这也是我今后要努力的方向. 下面是马老师坦克大战的视频集合 百度网盘链接 提取码:302w 以下是我的GitHub地址, 该仓库下有基础版本和改进版本. 基础版本完成了视频教学中的所有内容, 改进版本也就是最新版本则是个人在基础版本上作出的一些改进, 比如加入图片等. 基础版本地址 改进版本地址

优秀的个人博客,低调大师

C++实用编程——坦克大战小游戏

我们直接看代码吧,适于win XP和win 7,win 10 画质有些毒瘤 #include <stdio.h> #include <windows.h> #include <time.h> //里规格:长39*2=78 (真坐标)(假坐标宽为39) 高39 //外规格:长41*2=82 (真坐标)(假坐标宽为41) 高41 #define UP 1 #define DOWN 2 #define LEFT 3 #define RIGHT 4 #define MAX_LEVEL 8 #define BULLET_NUM 20 #define MAX_LIFE 4 //程序中未写入函数参数表中且未说明的变量只有map二维数组,level_info数组和level /* 此程序中涉及的x,y类的坐标值,分为以下两种: 假坐标:这里的坐标指的是以一个■长度为单位的坐标,而不是真正的coord坐标 (用于map数组的坐标) 真坐标:头文件自带的坐标结构coord中的坐标(也可以说是控制台里的真正坐标值) 区别:纵坐标y两值一致,假横坐标x值与真正coord横坐标(真坐标)关系是 x * 2 = coord 横坐标 coord横坐标既指GoTo函数中的x参数,因为本程序游戏界面以一个■长度为基本单位, 可以说涉及的coord横坐标全是偶数。既假坐标要变真坐标(变真坐标才能发挥真正作用),横坐标须乘以2 */ typedef struct //这里的出现次序指的是一个AI_tank变量中的次序,游戏共有四个AI_tank变量 { //∵设定每个AI_tank每种特殊坦克只出现一次 ∴fast_tank & firm_tank 最多出现次数不超过1 int fast_tank_order; //fast_tank出现的次序(在第fast_tank_order次复活出现,从第0次开始),且每个AI_tank只出现一次 int firm_tank_order; //firm_tank出现的次序,同上 } LevInfo; //关卡信息(准确说是该关出现的坦克信息) LevInfo level_info [MAX_LEVEL] = {{-1,-1},{3,-1},{-1,3},{2,3},{2,3},{2,3},{2,3},{2,3}}; //初始化,-1代表没有该类型坦克 typedef struct //子弹结构体 { int x,y; //子弹坐标,假坐标 int direction; //子弹方向变量 bool exist; //子弹存在与否的变量,1为存在,0不存在 bool initial; //子弹是否处于建立初状态的值,1为处于建立初状态,0为处于非建立初状态 bool my; //区分AI子弹与玩家子弹的标记,0为AI子弹,1为玩家(我的)子弹 } Bullet; Bullet bullet [BULLET_NUM]; //考虑到地图上不太可能同时存在20颗子弹,所以数组元素设置20个 typedef struct //坦克结构体 { int x,y; //坦克中心坐标 int direction; //坦克方向 int color; //颜色参方向数,1到6分别代表不同颜色,具体在PrintTank函数定义有说明 int model; //坦克图案模型,值为1,2,3,分别代表不同的坦克图案,0为我的坦克图案,AI不能使用 int stop; //只能是AI坦克使用的参数,非0代表坦克停止走动,0为可以走动 int revive; //坦克复活次数 int num; //AI坦克编号(固定值,为常量,初始化函数中定下)0~3 int CD; //发射子弹冷却计时 bool my; //是否敌方坦克参数,我的坦克此参数为1,为常量 bool alive; //存活为1,不存活为0 } Tank; Tank AI_tank[4] , my_tank; //my_tank为我的坦克,Ai_tank 代表AI坦克 //∵所有的函数都有可能对全局变量map进行读写(改变), //∴函数中不另说明是否会对全局变量map读写 //基本操作与游戏辅助函数 void GoToxy(int x,int y); //光标移动 void HideCursor(); //隐藏光标 void keyboard (); //接受键盘输入 void Initialize(); //初始化(含有对多个数据的读写) void Stop(); //暂停 void Getmap(); //地图数据存放与获取 void Frame (); //打印游戏主体框架 void PrintMap(); //打印地图(地图既地图障碍物)(含对level的读取) void SideScreen (); //副屏幕打印 void GameCheak(); //检测游戏输赢 void GameOver( bool home ); //游戏结束 void ClearMainScreen(); //主屏幕清屏函数∵system("cls")后打印框架有一定几率造成框架上移一行的错误∴单独编写清屏函数 void ColorChoose(int color); //颜色选择函数 void NextLevel(); //下一关(含有对level全局变量的读写) //子弹部分 void BuildAIBullet(Tank *tank); //AI坦克发射子弹(含有对my_tank的读取,只读取了my_tank坐标) void BuildBullet (Tank tank); //子弹发射(建立)(人机共用)(含全局变量bullet的修改)我的坦克发射子弹直接调用该函数,AI通过AIshoot间接调用 void BulletFly (Bullet bullet[BULLET_NUM]); //子弹移动和打击(人机共用), void BulletHit (Bullet* bullet); //子弹碰撞(人机共用)(含Tank全局变量的修改),只通过BulletFly调用,子弹间的碰撞不在本函数,子弹间碰撞已在BulletShoot中检测并处理 void PrintBullet (int x,int y,int T); //打印子弹(人机共用) void ClearBullet (int x,int y,int T); //清除子弹(人机共用) int BulletCheak (int x,int y); //判断子弹前方情况(人机共用) //坦克部分 void BuildAITank (int* position, Tank* AI_tank); //建立AI坦克 void BuildMyTank (Tank* my_tank); //建立我的坦克 void MoveAITank (Tank* AI_tank); //AI坦克移动 void MoveMyTank (int turn); //我的坦克移动,只通过keyboard函数调用,既键盘控制 void ClearTank (int x,int y); //清除坦克(人机共用) void PrintTank (Tank tank); //打印坦克(人机共用) bool TankCheak (Tank tank,int direction); //检测坦克dirtection方向的障碍物,返值1阻碍,0 畅通 int AIPositionCheak (int position); //检测AI坦克建立位置是否有障碍物AIPositionCheak //DWORD WINAPI InputX(LPVOID lpParameter); //声明线程函数,用于检查X键输入并设置X键的输入冷却时间 //注意map数组应是纵坐标在前,横坐标在后,既map[y][x],(∵数组行长度在前,列长度在后) //map里的值: 个位数的值为地图方块部分,百位数的值为坦克,子弹在map上没有值(子弹仅仅是一个假坐标) //map里的值: 0为可通过陆地,1为红砖,2黄砖,5为水,100~103为敌方坦克,200为我的坦克, //全局变量 int map[41][41]; //地图二维数组 int key_x; // X键是否被“读入”的变量,也是子弹是否可以发射的变, int bul_num; //子弹编号 int position; //位置计数,对应AI坦克生成位置,-1为左位置,0为中间,1为右,2为我的坦克位置 int speed=7; //游戏速度,调整用 int level=1; //游戏关卡数 int score=0; //游戏分数 int remain_enemy; //剩余敌人(未出现的敌人) char* tank_figure[4][3][4]= { { {"◢┃◣", "◢━◣", "◢┳◣", "◢┳◣"}, {"┣●┫", "┣●┫", "━●┃", "┃●━"}, {"◥━◤", "◥┃◤", "◥┻◤", "◥┻◤"} }, { {"┏┃┓", "┏┳┓", "┏┳┓", "┏┳┓"}, {"┣●┫", "┣●┫", "━●┫", "┣●━"}, {"┗┻┛", "┗┃┛", "┗┻┛", "┗┻┛"} }, { {"┏┃┓", "◢━◣", "┏┳◣", "◢┳┓"}, {"┣●┫", "┣●┫", "━●┃", "┃●━"}, {"◥━◤", "┗┃┛", "┗┻◤", "◥┻┛"} }, { {"╔┃╗", "╔╦╗", "╔╦╗", "╔╦╗"}, {"╠█╣", "╠█╣", "━█╣", "╠█━"}, {"╚╩╝", "╚┃╝", "╚╩╝", "╚╩╝"} } }; int main () //主函数 { int i; unsigned int interval[12]={1,1,1,1,1,1,1,1,1,1,1,1} ; //间隔计数器数组,用于控制速度 srand(time(NULL)); //设置随机数种子(若不设置种子而调用rand会使每次运行的随机数序列一致)随机数序列指:如首次调用rand得到1,第二次得2,第三次3,则此次随机数序列为1,2,3 HideCursor(); //隐藏光标 system("mode con cols=112 lines=42"); //控制窗口大小 Frame (); //打印游戏主体框架 Initialize(); //初始化,全局变量level初值便是1 // HANDLE h1 , h2 ; //定义句柄变量 for(;;) { if(interval[0]++%speed==0) //速度调整用,假设interval[0]为a, 语句意为 a % 2==0,a=a+1; { GameCheak(); //游戏胜负检测 BulletFly ( bullet ); for(i=0 ; i<=3 ; i++) //AI坦克移动循环 { if(AI_tank[i].model==2 && interval[i+1]++%2==0) //四个坦克中的快速坦克单独使用计数器1,2,3,4 MoveAITank( & AI_tank[i]); if(AI_tank[i].model!=2 && interval[i+5]++%3==0) //四个坦克中的慢速坦克单独使用计数器5,6,7,8 MoveAITank( & AI_tank[i]); } for(i=0;i<=3;i++) //建立AI坦克部分 if(AI_tank[i].alive==0 && AI_tank[i].revive<4 && interval[9]++%90==0) //一个敌方坦克每局只有4条命 { //如果坦克不存活。计时,每次建立有间隔 1750 ms BuildAITank( &position, & AI_tank[i] ); //建立AI坦克(复活) break; //每次循环只建立一个坦克 } for(i=0;i<=3;i++) if(AI_tank[i].alive) BuildAIBullet(&AI_tank[i]); //AIshoot自带int自增计数CD,不使用main中的CD interval if(my_tank.alive && interval[10]++%2==0 ) keyboard (); if(my_tank.alive==0 && interval[11]++%30==0 && my_tank.revive < MAX_LIFE) BuildMyTank( &my_tank ); } Sleep(5); } return 0; } /*//这里的多线程暂时不用 //x键用于子弹发射,x键的冷却时间不能和上下左右一同设置,那样就太快了 DWORD WINAPI InputX(LPVOID lpParameter) //如果不用多线程运行,那么在x键冷却时间内程序会因Sleep将会挂起,暂停运行 { //因为只有一个变量改变,而且变量改变先后顺序是显而易见的,所以不必设置缓冲区 for(;;) { if(GetAsyncKeyState( 88 )& 0x8000) //88为x键键值,当摁下x并且x键处于可输入状态 { key_x=1; // X键是否允许被“读入”的变量,也是子弹是否可以发射的变量 Sleep(600); // 子线程Sleep中,x就不能被"读入",主线程每操作完一次子弹发射,key_x会归零 } Sleep(10); } return 0; }*/ void keyboard () { // kbhit() getch() 用法可用但是不好用 /* 函数功能:该函数判断在此函数被调用时,某个键是处于UP状态还是处于DOWN状态,及前次调用GetAsyncKeyState函数后, 是否按过此键.如果返回值的最高位被置位,那么该键处于DOWN状态;如果最低位被置位,那么在前一次调用此函数后,此键被按过, 否则表示该键没被按过. 这里GetAsyncKeyState比 kbhit() + getch() 好用,操作更顺畅. GetAsyncKeyState的返回值表示两个内容, 一个是最高位bit的值,代表这个键是否被按下。一个是最低位bit的值,代表上次调用GetAsyncKeyState后,这个键是否被按下。 &为与操作,&0x8000就是判断这个返回值的高位字节。如果high-order bit是1,则是按下状态,否则是弹起状态,为0 */ int count=0; if (GetAsyncKeyState(VK_UP)& 0x8000) MoveMyTank( UP ); else if (GetAsyncKeyState(VK_DOWN)& 0x8000) MoveMyTank( DOWN ); else if (GetAsyncKeyState(VK_LEFT)& 0x8000) MoveMyTank( LEFT ); else if (GetAsyncKeyState(VK_RIGHT)& 0x8000) MoveMyTank( RIGHT ); else if (GetAsyncKeyState( 0x1B )& 0x8000) // Esc键 exit(0); //退出程序函数 else if (GetAsyncKeyState( 0x20 )& 0x8000) //空格 Stop(); else if (count++%7==0) //这里添加计数器是为了防止按键粘连不能达到微调效果 { if (speed>1 && GetAsyncKeyState( 0x6B )& 0x8000) // +键 { speed--; GoToxy(102,11); //在副屏幕打印出当前速度 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_RED); printf("%d ",21-speed); //副屏幕显示的速度为1~10 } else if (speed<20 && GetAsyncKeyState( 0x6D )& 0x8000) // - 键 { speed++; GoToxy(102,11); //在副屏幕打印出当前速度 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_RED); printf("%d ",21-speed); //副屏幕显示的速度为1~10 } } if(my_tank.CD==7) { if(GetAsyncKeyState( 88 )& 0x8000) { BuildBullet(my_tank); my_tank.CD=0; } } else my_tank.CD++; } void BuildAIBullet(Tank *tank) //AI子弹发射(建立)含有对my_tank的读取 { if(tank->CD==15) { if(!(rand()%11)) //冷却结束后在随后的每个游戏周期中有10分之一的可能发射子弹 { BuildBullet(*tank); tank->CD=0; } } else tank->CD++; if(tank->CD >= 14) //AI强化部分,在冷却到达一定范围即可使用 { if(tank->y==38 ) //如果坦克在底部(这个最优先) { if(tank->x < 20) //在老家左边 { if(tank->direction==RIGHT) //坦克方向朝左 { BuildBullet(*tank); //发射子弹 tank->CD=0; } } else //在老家右边 if(tank->direction==LEFT) //坦克方向朝右 { BuildBullet(*tank); //发射子弹 tank->CD=0; } } else if(tank->x==my_tank.x+1 || tank->x==my_tank.x || tank->x==my_tank.x-1) //AI坦克在纵向上"炮口"对准我的坦克 { if(tank->direction==DOWN && my_tank.y > tank->y || tank->direction==UP && my_tank.y < tank->y) { //若是AI朝下并且我的坦克在AI坦克下方(数值大的在下面)或者AI朝上我的坦克在AI上方 int big=my_tank.y , smal=tank->y , i; if(my_tank.y < tank->y) { big=tank->y; smal=my_tank.y; } for(i=smal+2;i<=big-2;i++) //判断AI炮口的直线上两坦克间有无障碍 if(map[i][tank->x]!=0 || map[i][tank->x]!=5) //若有障碍 break; if(i==big-1) //若i走到big-1说明无障碍 { BuildBullet(*tank); //则发射子弹 tank->CD=0; } } } else if(tank->y==my_tank.y+1 || tank->y==my_tank.y || tank->y==my_tank.y-1) //AI坦克在横向上"炮口"对准我的坦克 { if(tank->direction==RIGHT && my_tank.x > tank->x || tank->direction==LEFT && my_tank.x < tank->x) { //若是AI朝右并且我的坦克在AI坦克右方(数值大的在下面)或者AI朝左我的坦克在AI左方 int big=my_tank.y , smal=tank->y , i; if(my_tank.x < tank->x) { big=tank->x; smal=my_tank.x; } for(i=smal+2;i<=big-2;i++) //判断AI炮口的直线上两坦克间有无障碍 if(map[tank->y][i]!=0 || map[tank->y][i]!=5) //若有障碍 break; if(i==big-1) //若i走到big-1说明无障碍 { BuildBullet(*tank); //则发射子弹 tank->CD=0; } } } } } void BuildBullet(Tank tank) //子弹发射(建立),传入结构体Tank,这里包含改变了全局变量结构体bullet { //∵实现方式为顺序循环建立子弹,每次调用改变的bullet数组元素都不同 switch(tank.direction) //∴为了方便,不将bullet放入参数,bullet作为全局变量使用 { case UP : bullet [bul_num].x = tank.x; bullet [bul_num].y = tank.y-2; bullet [bul_num].direction=1; break; case DOWN : bullet [bul_num].x = tank.x; bullet [bul_num].y = tank.y+2; bullet [bul_num].direction=2; break; case LEFT : bullet [bul_num].x = tank.x-2; bullet [bul_num].y = tank.y; bullet [bul_num].direction=3; break; case RIGHT : bullet [bul_num].x = tank.x+2; bullet [bul_num].y = tank.y; bullet [bul_num].direction=4; break; } bullet [bul_num].exist = 1; //子弹被建立,此值为1则此子弹存在 bullet [bul_num].initial = 1; //子弹处于初建立状态 bullet [bul_num].my=tank.my; //如果是我的坦克发射的子弹bullet.my=1,否则为0 bul_num++; if(bul_num==BULLET_NUM) //如果子弹编号增长到20号,那么重头开始编号 bul_num=0; //考虑到地图上不可能同时存在20颗子弹,所以数组元素设置20个 } void BulletFly(Bullet bullet[BULLET_NUM]) //子弹移动和打击 { //含有全局变量Bullet的改变 for(int i =0; i<BULLET_NUM;i++) { if(bullet [i].exist) //如果子弹存在 { if(bullet [i].initial==0) //如果子弹不是初建立的 { if(map[bullet[i].y] [bullet[i].x]==0 || map[bullet[i].y] [bullet[i].x]==5) //如果子弹坐标当前位置无障碍 ClearBullet( bullet[i].x , bullet[i].y , BulletCheak(bullet[i].x , bullet[i].y )); //抹除子弹图形 switch(bullet [i].direction) //然后子弹坐标变化(子弹变到下一个坐标) { case UP :(bullet [i].y)--;break; case DOWN :(bullet [i].y)++;break; case LEFT :(bullet [i].x)--;break; case RIGHT :(bullet [i].x)++;break; } } int collide = BulletCheak ( bullet [i].x , bullet [i].y ); //判断子弹当前位置情况,判断子弹是否碰撞,是否位于水面上。 if( collide ) //如果检测到当前子弹坐标无障碍(无碰撞)(包括在地面上与在水面上) PrintBullet( bullet[i].x , bullet[i].y , collide); //则打印子弹,若有碰撞则不打印 else BulletHit( & bullet [i] ); //若有碰撞则执行子弹碰撞函数 if(bullet [i].initial) //若子弹初建立,则把初建立标记去除 bullet [i].initial = 0; for(int j=0; j< BULLET_NUM ; j++) //子弹间的碰撞判断,若是我方子弹和敌方子弹碰撞则都删除,若为两敌方子弹则无视 if(bullet [j].exist && j!=i && (bullet[i].my || bullet[j].my) && bullet[i].x==bullet[j].x && bullet[i].y==bullet[j].y) { //同样的两颗我方子弹不可能产生碰撞 bullet [j].exist=0; bullet [i].exist=0; ClearBullet( bullet[j].x , bullet[j].y , BulletCheak(bullet[j].x , bullet[j].y )); //抹除j子弹图形,子弹i图形已被抹除 break; } } } } void BulletHit(Bullet* bullet) //含有Tank全局变量的修改,子弹间的碰撞不在本函数,子弹间碰撞已在BulletShoot中检测并处理 { //∵每次打中的坦克都不一样,不可能把所有坦克放在参数表中 int x=bullet->x; //∴这里的Tank使用全局变量 int y=bullet->y; //这里传入的值是子弹坐标,这两个值不需要改变 int i; if(map[y][x]==1 || map[y][x]==2) //子弹碰到砖块 { if(bullet->direction==UP || bullet->direction==DOWN) //如果子弹是纵向的 for(i = -1 ; i<=1 ; i++) if(map[y][x+i]==1 || map[y][x+i]==2) //如果子弹打中砖块两旁为砖块,则删除砖,若不是(一旁为坦克或其他地形)则忽略 { map[y][x+i]=0; //砖块碎 GoToxy(2*x+2*i,y); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED); //背景黑色 printf(" "); } if(bullet->direction==LEFT || bullet->direction==RIGHT) //若子弹是横向的 (与子弹纵向实现同理) for(i = -1 ; i<=1 ; i++) if(map[y+i][x]==1 || map[y+i][x]==2) { map[y+i][x]=0; GoToxy(2*x,y+i); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED); //背景黑色 printf(" "); } bullet->exist=0; //这颗子弹已经不存在了 } else if(map[y][x]==4 || map[y][x]==6 ) //子弹碰到边框或者不可摧毁方块 bullet->exist=0; else if(bullet->my && map[y][x]>=100 && map[y][x]<104 ) //若我的子弹碰到了敌方坦克 { int num = map[y][x]%100; //map[y][x]%100 等同于 tank.num ,可通过map值读取该坦克信息 if(AI_tank[num].model==3 && AI_tank[num].color==2) //若为firm tank,且color==2。该坦克为绿色,表明没有受到伤害 AI_tank[num].color=3; //则变成黄色,color=3为黄色 else if (AI_tank[num].model==3 && AI_tank[num].color==3) AI_tank[num].color=4; //4为红色 else //其他类型的坦克或者firm tank为红色的情况 { AI_tank[num].alive=0; ClearTank(AI_tank[num].x , AI_tank[num].y); //清除该坦克 } bullet->exist=0; score+=100; GoToxy(102,5); //在副屏幕上打印出分数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); printf("%d ",score); } else if(map[y][x]==200 && bullet->my==0 ) //若敌方子弹击中我的坦克 { my_tank.alive=0; ClearTank(my_tank.x , my_tank.y); bullet->exist=0; my_tank.revive++; //我的坦克复活次数+1(∵我的坦克复活次数与生命值有关∴放在这里自减) score-=100; //分数减少 GoToxy(102,5); //在副屏幕上打印出分数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); printf("%d ",score); GoToxy(102,7); //在副屏幕打印出我的剩余生命值 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); printf("%d ", MAX_LIFE-my_tank.revive); } // else if(bullet->my==0 && map[y][x]>=100 && map[y][x]<104) //敌方子弹击中敌方坦克,可以设置两种子弹运行方式,这种暂时不用 // bullet->exist=0; else if(map[y][x]==9) //子弹碰到家(无论是谁的子弹) { bullet->exist=0; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); GoToxy(38,37); printf(" "); GoToxy(38,38); printf("◢◣ "); GoToxy(38,39); printf("███"); GameOver(1); //游戏结束,传入1代表老家被毁 } } int BulletCheak (int x,int y) //判断子弹当前位置情况,判断子弹是否碰撞,是否位于水面上。 { //有障碍返回0,无障碍且子弹在地面返回1,子弹在水面上返回2 if(map[y][x]==0) return 1; else if(map[y][x]==5) return 2; else return 0; } void PrintBullet (int x,int y,int T) //当前坐标BulletCheak 的值做参量 T { if(T==1) // T==1 表示子弹当前坐标在陆地上 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); else if(T==2) // T==2 表示子弹当前坐标在水面上 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY|BACKGROUND_BLUE); GoToxy(2*x,y); printf(""); } void ClearBullet(int x,int y,int T) //当前坐标BulletCheak 的值做参量 T { GoToxy(2*x,y); if(T==2) // T==2 表示子弹当前坐标在水面上 { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|BACKGROUND_BLUE|FOREGROUND_BLUE|FOREGROUND_GREEN); printf("~"); } else if(T==1) // T==1 表示子弹当前坐标在陆地上 { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE); printf(" "); } } //position为坦克生成位置,-1为左位置,0为中间,1为右,2为我的坦克位置 void BuildAITank(int* position, Tank* AI_tank) //执行一次该函数只建立一个坦克 { //rand函数公式:0<=rand()%(a+1)<=a 0+m<=rand()%(n-m+1)+m<=n //rand函数实现1到n:1<=rand()%(n)+1<=n if(AIPositionCheak(*position)) //若此位置无障碍,可生成。position参数详见AIPositionCheak函数定义 { AI_tank->x= 20 + 18*(*position); //20 + 18 * position 对应三个生成位置的x假坐标 AI_tank->y=2; if(AI_tank->revive==level_info[level-1].firm_tank_order) //坦克出现(复活)次序==关卡信息(level_info)中firm tank的出现次序 { AI_tank->model = 3; //3为firm tank的模型(外观) AI_tank->color = 2; //颜色参数2为绿色,具体详见函数ColorChoose } else if(AI_tank->revive==level_info[level-1].fast_tank_order) //同上if,这里是fast_tank的 { AI_tank->model = 2; AI_tank->color = rand()%6+1; //若不是firm tank则随机颜色,颜色参数为1~6,分别代表不同颜色,详见函数ColorChoose } else //普通坦克 { AI_tank->model = 1; AI_tank->color = rand()%6+1; //若不是firm tank则随机颜色 } AI_tank->alive = 1; //坦克变为存在 AI_tank->direction = 2 ; //方向朝下 AI_tank->revive++; //复活次数+1 PrintTank(*AI_tank); (*position)++; remain_enemy--; GoToxy(102,9); //在副屏幕上打印剩余坦克数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED); printf("%d ",remain_enemy); if(*position==2) //position只能为0,1,-1,这里position循环重置 *position = -1; return ; //若生成了一辆坦克,则结束该函数 } } int AIPositionCheak( int position ) //position为坦克生成位置2为我的坦克位置,其余为AI位,-1为左位,0为中间位置,1为右位 { int x,y; if(position==2) //2为我的坦克位置,现在暂时用不到 x=15,y=38; else y=2 , x= 20 + 18 * position ; //20 + 18 * position 对应三个生成位置的x假坐标 for(int i=0;i<3;i++) for(int j=0;j<3;j++) if( map[y+j-1][x+i-1]!=0) //如果遍历的九宫格里有障碍物 return 0; //则返回0,表示此生成位置有阻碍 return 1; //否则生成1,表示此生成位置无阻碍 } void MoveAITank(Tank* AI_tank) //AI专用函数,该函数主要为AI加强 { if(AI_tank->alive) //如果坦克活着 { if(AI_tank->stop!=0) //坦克是否停止运动的判断,若stop参数不为0 { AI_tank->stop--; //则此坦克本回合停止运动 return; } if( !(rand()%23) ) //22分之1的概率执行方向重置 { AI_tank->direction = rand()%4+1; if( rand()%3 ) //在方向重置后有2分之1的概率停止走动3步的时间 { AI_tank->stop=2; return; } } ClearTank (AI_tank->x , AI_tank->y); if(TankCheak ( *AI_tank , AI_tank->direction)) //如果前方无障碍 switch ( AI_tank->direction ) { case UP : AI_tank->y--; break; //上前进一格 case DOWN : AI_tank->y++; break; //下前进一格 case LEFT : AI_tank->x--; break; //左前进一格 case RIGHT: AI_tank->x++; break; //右前进一格 } else //前方有障碍 { if(!(rand()%4)) //3分之1的概率乱转 { AI_tank->direction=rand()%4+1; AI_tank->stop=2; //乱转之后停止走动3步的时间 PrintTank(*AI_tank); return; //∵continue会跳过下面的打印函数,∴这里先打印 } else //另外3分之2的几率选择正确的方向 { int j; for(j=1;j<=4;j++) if(TankCheak ( *AI_tank , j )) //循环判断坦克四周有无障碍,此函数返值1为可通过 break; if(j==5) //j==5说明此坦克四周都有障碍物,无法通行 { PrintTank(*AI_tank); return; //则跳过下面的while循环以防程序卡死 } while(TankCheak ( *AI_tank , AI_tank->direction) == 0) //如果前方仍有障碍 AI_tank->direction=(rand()%4+1); //则换个随机方向检测 } } PrintTank(*AI_tank); //打印AI坦克 } } void BuildMyTank (Tank* my_tank) //建立我的坦克 { my_tank->x=15; my_tank->y=38; my_tank->stop=NULL; my_tank->direction=1; my_tank->model=0; my_tank->color=1; my_tank->alive=1; my_tank->my=1; my_tank->CD=7; PrintTank (*my_tank) ; //打印我的坦克 } void MoveMyTank(int turn ) //玩家专用函数,turn为keyboard函数里因输入不同方向键而传入的不同的值 { ClearTank(my_tank.x , my_tank.y); //map 数组中“我的坦克”参数清除工作已在此函数中完成 my_tank.direction=turn; //将键盘输入的方向值传入我的坦克方向值 if(TankCheak ( my_tank , my_tank.direction )) //若此时我的坦克当前方向上无障碍 switch (turn) { case UP : my_tank.y--; break; //上前进一格 case DOWN : my_tank.y++; break; //下前进一格 case LEFT : my_tank.x--; break; //左前进一格 case RIGHT: my_tank.x++; break; //右前进一格 } //若坦克当前方向上有障碍则跳过坐标变化直接打印该转向的坦克 PrintTank (my_tank); } bool TankCheak(Tank tank,int direction) //检测坦克前方障碍函数,参量为假坐标。返值1为可通过,返值0为阻挡(人机共用) { switch(direction) //direction变量 1上,2下,3左,4右 { case UP: if (map[tank.y-2][tank.x]==0 && map[tank.y-2][tank.x-1]==0 && map[tank.y-2][tank.x+1]==0) return 1; else return 0; case DOWN: if (map[tank.y+2][tank.x]==0 && map[tank.y+2][tank.x-1]==0 && map[tank.y+2][tank.x+1]==0) return 1; else return 0; case LEFT: if (map[tank.y][tank.x-2]==0 && map[tank.y-1][tank.x-2]==0 && map[tank.y+1][tank.x-2]==0) return 1; else return 0; case RIGHT: if (map[tank.y][tank.x+2]==0 && map[tank.y-1][tank.x+2]==0 && map[tank.y+1][tank.x+2]==0) return 1; else return 0; default: printf("错误!!"); Sleep(5000); return 0; } } void ClearTank(int x,int y) //清除坦克函数(人机共用) { for(int i=0;i<3;i++) for(int j=0;j<3;j++) { //将坦克占用的地图上的九格去掉 map[y+j-1][x+i-1]=0; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN); GoToxy(2*x+2*j-2,y+i-1); printf(" "); } } void PrintTank(Tank tank) //打印坦克(人机共用) 由于读取的Tank参数较多,故就不将参数一一传入了 { // tank.color参数对应不同的颜色,范围 1 ~ 6 ColorChoose(tank.color); //颜色选择函数 定义一个数组里装着字符指针(既装字符串)的数组指针(指向一维数组首地址的指针) char *(*tankF)[4] = tank_figure[tank.model]; //将二维数组首址赋初值给数组指针 model==0为我的坦克,4为电脑1坦克,8为电脑2,类推 for(int i = 0; i < 3; i++) { GoToxy((tank.x-1)*2 , tank.y-1+i); //在坦克中心坐标的左边,上中下三行打印 printf("%s", tankF[i][tank.direction-1]); //打印的是地址,地址既字符串 for(int j=0;j<3;j++) if(tank.my) //若为我的坦克 map[tank.y+j-1][tank.x+i-1]=200; //在map上把"坦克"九格填满代表敌我坦克的参数。敌方此值为100~103,我方为200 else map[tank.y+j-1][tank.x+i-1]=100+tank.num; //这样可以通过map值读取坦克编号,读取操作在BulletHit 函数 } } void HideCursor() //隐藏光标 { //CONSOLE_CURSOR_INFO结构体包含控制台光标的信息,DWORD dwSize光标百分比厚度(1~100)和BOOL bVisible光标是否可见 CONSOLE_CURSOR_INFO cursor_info={1,0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info); //SetConsoleCursorInfo用来设置指定的控制台光标的大小和可见性。 } void GoToxy(int x,int y) //光标移动函数,X表示横坐标,Y表示纵坐标。 { COORD coord; //使用头文件自带的坐标结构 coord.X=x; //这里将int类型值传给short,不过程序中涉及的坐标值均不会超过short范围 coord.Y=y; HANDLE a=GetStdHandle(STD_OUTPUT_HANDLE); //获得标准输出句柄 SetConsoleCursorPosition(a,coord); //以标准输出的句柄为参数设置控制台光标坐标 } void ColorChoose(int color) //颜色选择函数 { switch(color) { case 1: //天蓝色(我的坦克颜色) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE); break; case 2: //绿色 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); break; case 3: //黄色 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN); break; case 4: //红色 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED); break; case 5: //紫色 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE); break; case 6: //白色 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); break; case 7: //深蓝色(∵颜色深难与黑色背景辨识度不高 ∴坦克颜色不选用此颜色),只用在字体颜色闪烁中 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE); break; } } void Stop() //暂停 { int color=1,timing=0; while(1) { if(timing++%30==0) { ColorChoose(color); //颜色选择 GoToxy(100,13); //副屏幕打印 printf("游戏暂停"); GoToxy(88,17); printf("按回车键回到游戏"); GoToxy(88,18); printf("或按 Esc键退出游戏"); if(++color==8) color=1; } if (GetAsyncKeyState( 0xD )& 0x8000) //回车键 { GoToxy(100,13); //副屏幕打印 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE); printf("正在进行"); //覆盖掉原来的提示 GoToxy(88,17); printf(" "); GoToxy(88,18); printf(" "); break; } else if(GetAsyncKeyState( 0x1B )& 0x8000) //Esc键退出 exit(0); Sleep(20); } } void ClearMainScreen() //主屏幕清屏函数,因使用system("cls");再打印框架有一定几率造成框架上移一行的错误,所以单独编写清屏函数 { for(int i=1;i<40;i++) { GoToxy(2,i); printf(" "); } } void Frame () //打印游戏主体框架 { //SetConsoleTextAttribute为设置文本颜色和文本背景颜色函数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY); printf(" ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ "); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY|FOREGROUND_BLUE); printf(" ▂▂▂▂▂▂▂▂▂▂▂▂▂ \n"); for(int i=0;i<14;i++) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_INTENSITY); printf("▕ ▏"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY|FOREGROUND_BLUE); printf(" | |\n"); } SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_INTENSITY); printf("▕ ▏"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY|FOREGROUND_BLUE); printf(" |═════════════|\n"); for(int i=0;i<24;i++) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_INTENSITY); printf("▕ ▏"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY|FOREGROUND_BLUE); printf(" | |\n"); } SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_INTENSITY); printf(" ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ "); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY| FOREGROUND_BLUE); printf(" ﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊\n"); SideScreen (); //打印副屏幕 } void PrintMap() // 打印地图(地图既地图障碍物) { for(int j=0;j<41;j++) for(int i=0;i<41;i++) if(map[i][j]==6) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN |FOREGROUND_RED|FOREGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED|BACKGROUND_BLUE); GoToxy(2*j,i); printf("■"); } else if(map[i][j]==2) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED|BACKGROUND_GREEN|BACKGROUND_RED); GoToxy(2*j,i); printf("▓"); } else if(map[i][j]==1) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|BACKGROUND_GREEN|BACKGROUND_RED); GoToxy(2*j,i); printf("▓"); } else if(map[i][j]==5) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|BACKGROUND_BLUE|FOREGROUND_BLUE|FOREGROUND_GREEN); GoToxy(2*j,i); printf("~"); } SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); GoToxy(38,37); printf("◣◢"); GoToxy(38,38); printf("███"); //∵无论地图怎么变,家所在位置不变,且家的字符多种,不方便用上述方式打印 GoToxy(38,39); printf("◢█◣"); //∴直接打印(且家的map值与符号无关) } void GetMap() //地图存放函数 { //map里的值: 个位数的值为地图方块部分,百位数的值为坦克 int i ,j; //map里的值: 0为可通过陆地,1为红砖,2待定,5为水,100为敌方坦克,200为我的坦克, int Map[8][41][41]= { { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,6,6,6,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,6,6,6,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,6,6,6,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,4}, {4,6,6,6,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,2,2,2,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,2,2,2,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,2,2,2,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,4}, {4,1,1,1,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,6,6,6,0,0,0,4}, {4,1,1,1,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,6,6,6,0,0,0,4}, {4,1,1,1,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,1,1,1,6,6,6,6,6,6,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,1,1,1,6,6,6,6,6,6,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,1,1,1,6,6,6,6,6,6,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,6,6,6,1,1,1,2,2,2,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,6,6,6,1,1,1,2,2,2,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,6,6,6,1,1,1,2,2,2,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,6,6,6,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,6,6,6,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,6,6,6,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,6,6,6,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,6,6,6,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,6,6,6,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,1,1,1,6,6,6,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,1,1,1,6,6,6,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,2,2,2,1,1,1,6,6,6,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,6,6,6,6,6,6,6,6,6,2,2,2,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,6,6,6,6,6,6,6,6,6,2,2,2,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,6,6,6,6,6,6,6,6,6,2,2,2,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,6,6,6,6,6,6,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,6,6,6,6,6,6,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,6,6,6,6,6,6,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,1,1,1,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,1,1,1,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,1,1,1,1,1,1,0,0,0,4}, {4,2,2,2,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,6,6,6,4}, {4,2,2,2,6,6,6,6,6,6,6,6,6,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,6,6,6,6,6,6,4}, {4,2,2,2,6,6,6,6,6,6,6,6,6,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,6,6,6,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,1,1,1,6,6,6,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,1,1,1,6,6,6,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,1,1,1,5,5,5,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,5,5,5,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,5,5,5,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,5,5,5,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,5,5,5,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,5,5,5,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,6,6,6,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,5,5,5,0,0,0,1,1,1,1,1,1,4}, {4,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,5,5,5,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,5,5,5,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,4}, {4,6,6,6,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,4}, {4,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,4}, {4,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,5,5,5,0,0,0,0,1,1,1,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,4}, {4,5,5,5,0,0,0,0,1,1,1,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,4}, {4,5,5,5,0,0,0,0,1,1,1,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,4}, {4,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,4}, {4,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,1,1,1,0,0,0,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,1,1,1,0,0,0,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,1,1,1,0,0,0,5,5,5,4}, {4,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,4}, {4,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,4}, {4,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,0,0,0,5,5,5,1,1,1,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,0,0,0,5,5,5,1,1,1,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,4}, {4,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,0,0,0,5,5,5,1,1,1,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,5,5,5,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,4}, {4,0,0,0,5,5,5,5,5,5,5,5,5,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,5,5,5,0,0,0,5,5,5,5,5,5,4}, {4,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,5,5,5,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,4}, {4,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,1,1,1,1,0,0,0,1,1,0,4}, {4,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,2,2,2,2,2,2,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,4}, {4,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,2,2,2,2,2,2,2,2,0,0,0,1,1,0,0,0,0,0,0,0,1,1,4}, {4,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,2,2,2,2,2,2,2,2,0,0,1,1,1,0,0,0,0,0,0,0,1,1,4}, {4,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0,0,0,0,0,0,0,1,1,4}, {4,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0,0,0,0,0,0,0,1,1,4}, {4,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0,0,0,0,0,0,0,1,1,4}, {4,1,1,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,2,6,6,6,6,6,6,2,2,2,2,1,1,1,0,0,0,0,0,1,1,1,4}, {4,1,1,0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,6,6,6,6,6,6,2,2,2,2,1,1,1,1,0,0,0,0,1,1,1,4}, {4,1,1,0,0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,6,6,6,6,6,6,2,2,2,2,1,1,1,1,0,0,0,1,1,1,1,4}, {4,0,1,1,0,0,0,0,0,0,1,1,1,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,1,1,1,0,0,1,1,1,1,4}, {4,0,1,1,1,0,0,0,0,1,1,1,1,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,1,1,1,1,1,1,1,1,1,4}, {4,0,0,1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,1,1,1,1,1,1,1,1,1,4}, {4,0,0,0,1,1,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,1,0,4}, {4,0,0,0,1,1,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,1,0,4}, {4,0,0,0,0,1,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,0,0,4}, {4,0,0,0,0,0,1,1,1,1,1,1,1,6,6,6,0,0,0,1,1,1,0,0,0,6,6,6,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,6,6,6,0,0,0,1,1,1,0,0,0,6,6,6,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,6,6,6,0,0,0,1,1,1,0,0,0,6,6,6,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,4}, {4,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,6,6,6,6,6,6,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,6,6,6,6,6,6,4}, {4,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,0,0,0,6,6,6,6,6,6,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,1,1,1,0,0,0,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,1,1,1,0,0,0,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,1,1,1,0,0,0,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,4}, {4,0,0,0,6,6,6,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,6,6,6,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,6,6,6,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,4}, {4,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,6,6,6,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,6,6,6,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,4}, {4,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,6,6,6,0,0,0,6,6,6,0,0,0,0,0,0,1,1,1,4}, {4,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,6,6,6,0,0,0,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, { {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,5,5,5,5,5,5,1,1,1,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,0,0,0,5,5,5,5,5,5,1,1,1,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,0,0,0,5,5,5,5,5,5,1,1,1,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,6,6,6,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,1,1,1,0,0,0,0,0,0,4}, {4,0,0,0,6,6,6,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,6,6,6,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,6,6,6,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,5,5,5,5,5,5,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,5,5,5,5,5,5,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,5,5,5,5,5,5,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,6,6,6,4}, {4,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,1,1,1,5,5,5,5,5,5,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,6,6,6,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,4}, {4,6,6,6,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,4}, {4,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,9,9,9,1,1,0,0,0,0,0,0,0,1,1,1,6,6,6,0,0,0,4}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4} }, }; for(i=0;i<41;i++) for(j=0;j<41;j++) map[i][j]=Map[level-1][i][j]; PrintMap(); //打印地图 } void GameOver(bool home) { int timing=0,color=1; while(1) { if(timing++%30==0) //游戏结束原因为生命值为0 { ColorChoose(color); //颜色选择 if(home) //游戏结束原因为老家被毁,则多打印一行字以提示玩家 { GoToxy(37,19); //主屏幕中心打印 printf("老家被毁!"); } GoToxy(37,20); //主屏幕中心打印 printf("游戏结束!"); GoToxy(100,13); //副屏幕打印 printf("游戏结束"); GoToxy(88,17); printf("请按回车键重新开始!"); GoToxy(88,18); printf("或按 Esc键退出游戏!"); if(++color==8) color=1; } if (GetAsyncKeyState( 0xD )& 0x8000) //回车键 { // system("cls"); //清屏,这里清屏后再次打印框架有一定几率造成框架上移一行的bug,因此选用自建清屏函数 // Frame (); //重新打印游戏框架 score-=500; //分数-500 ClearMainScreen(); //主屏清屏函数,无需再次打印框架 Initialize(); //从本关重新开始 break; } else if(GetAsyncKeyState( 0x1B )& 0x8000) //Esc键退出 exit(0); Sleep(20); } } void NextLevel() { int timing=0,color=1; level++; if(level<=MAX_LEVEL) while(1) { if(timing++%10==0) { ColorChoose(color); //颜色选择 GoToxy(37,20); //主屏幕中心打印 printf("恭喜过关!"); GoToxy(100,13); //副屏幕打印 printf("等待下关"); GoToxy(87,17); printf("请按回车键进入下一关!"); GoToxy(88,18); printf("或按 Esc键退出游戏!"); if(++color==8) color=1; } if (GetAsyncKeyState( 0xD )& 0x8000) //回车键 { GoToxy(88,17); //抹除副屏幕中的提示 printf(" "); GoToxy(88,18); printf(" "); ClearMainScreen(); //主屏清屏函数,无需再次打印框架 Initialize(); //初始化从下一关开始,level已++ break; } else if(GetAsyncKeyState( 0x1B )& 0x8000) //Esc键退出 exit(0); Sleep(20); } else //level>8 通关 while(1) { if(timing++%5==0) { ColorChoose(color); GoToxy(33,20); //主屏幕中心打印 printf("恭喜通过全部关卡!"); GoToxy(100,13); //副屏幕打印 printf("已通全关"); GoToxy(88,17); printf("恭喜通过全部关卡!"); GoToxy(88,19); printf("按 Esc键退出游戏!"); if(++color==8) color=1; } if(GetAsyncKeyState( 0x1B )& 0x8000) //Esc键退出 exit(0); Sleep(10); } } void GameCheak() { //剩余敌人为0且四坦克全部不存活 if(remain_enemy<=0 && !AI_tank[0].alive && !AI_tank[1].alive && !AI_tank[2].alive && !AI_tank[3].alive ) NextLevel(); //进入下一关 if(my_tank.revive>=MAX_LIFE) //我的生命值(复活次数)全部用完 MAX_LIFE GameOver(0); //游戏结束,传入0代表我的复活次数用光(生命值为0)。游戏结束有两种判断,另一种是老家被毁 } void SideScreen () //副屏幕 行(84起,110末,若双字符宽则在108打印最后一个字)列(11上屏末,13下屏初,39下屏末。为美观最好打在38) { // | 第 d 关 | " | |\n" GoToxy(93,2); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED); printf("第 关"); GoToxy(92,5); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_BLUE); printf("分 数:"); GoToxy(92,7); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); printf("生 命:"); GoToxy(86,9); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED); printf("剩余敌方坦克:"); GoToxy(86,11); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE); printf("当前游戏速度: %d",21-speed); GoToxy(86,13); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE); printf("当前游戏状态:"); GoToxy(94,19); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED); GoToxy(94,24); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED); printf("帮 助"); GoToxy(86,27); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); printf("方向键 ←↑→↓ 移动"); GoToxy(93,29); printf("x 键 射击"); GoToxy(89,31); printf("+ - 调整游戏速度"); GoToxy(90,33); printf("游戏速度范围1~20"); GoToxy(90,35); printf("回车键 暂停游戏"); GoToxy(90,37); printf("Esc键 退出游戏"); /* printf("帮 助"); //这是第二种详细说明的样式 GoToxy(86,21); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); printf("方向键 ←↑→↓ 移动"); GoToxy(93,23); printf("x 键 射击"); GoToxy(89,25); printf("+ - 调整游戏速度"); GoToxy(90,27); printf("游戏速度范围1~20"); GoToxy(90,29); printf("回车键 暂停游戏"); GoToxy(90,31); printf("Esc键 退出游戏"); GoToxy(86,33); printf("敌方坦克全部消灭则过关"); GoToxy(87,34); printf("己方坦克生命值为0 或"); GoToxy(86,35); printf("正下方的老家被毁则失败"); GoToxy(86,36); printf("己坦克与敌坦克子弹碰撞"); GoToxy(87,37); printf("则抵消,敌坦克间子弹碰"); GoToxy(86,38); printf("撞不抵消且可穿过敌坦克");*/ } void Initialize() //初始化 { remain_enemy=16; my_tank.revive=0; //我的坦克复活次数为0 position=0; bul_num=0; GetMap(); BuildMyTank( &my_tank ); for(int i=0;i<12;i++) //子弹初始化 { bullet [i].exist=0; bullet [i].initial=0; } for(int i=0;i<=3;i++) //AI坦克初始化 { AI_tank [i].revive=0; AI_tank [i].alive=0; //初始化坦克全是不存活的,BuildAITank()会建立重新建立不存活的坦克 AI_tank [i].stop=0; AI_tank [i].num=i; AI_tank [i].my=0; AI_tank [i].CD=0; } GoToxy(97,2); //在副屏幕上关卡数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN); printf("%d",level); GoToxy(102,5); //在副屏幕上打印分数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_BLUE); printf("%d ",score); GoToxy(102,7); //在副屏幕打印我的剩余生命值 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); printf("%d", MAX_LIFE-my_tank.revive); GoToxy(102,9); //在副屏幕上打印剩余坦克数 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED); printf("%d ",remain_enemy); GoToxy(100,13); //在副屏幕上打印状态 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_GREEN); printf("正在游戏"); } 很长,但是游戏效果很好。在游玩之余,多看看代码,不难学到许多windows.h的用途以及多函数编程,今天的分享也就到这里了,我们下次见!

优秀的个人博客,低调大师

python打造特别火的一个小游戏,16行代码实现3D撞球小游戏

以下是制作上面炫酷动画所需的全部代码: 我们需要三组刚体(当您在Blender的对象上打开一个刚体的属性时,Blender将模拟与其它刚体的碰撞): 1.平面 第2行代码创建了一个简单的平面,立方体将放置在该平面上。为了防止它因重力而坠落,我们将其设为“受体”[第4行代码]。 2. 圆环 [第11-12行]将第一个圆环的"Enabled"属性设置为false,防止由于重力而坠落。这样它就固定在那牵住整个链条。 3. 立方体 因为z循环[第13行]嵌套在x循环[第5行]中,我们将得到一个18X10的立方体组成的墙。 好了!当您点击时间线上的“播放”时,链条就会掉下来,撞上立方体并让它们飞起来! 现在我们让最后一个圆环变大一点,让它看起来更像一个实际的破坏球,而不是用一根链子打破墙壁。为了有更酷的碰撞效果,将第6-8行代码更改为: 这个python学习(q-u-n): 227-435-450 期待大家一起进群交流讨论,讲实话还是一个非常适合学习的地方的。各种入门资料啊,进阶资料啊,框架资料啊 等等 将第13-16行代码替换为: 并在代码的最开始处添加以下行,这样才能使用sin和cos三角函数: 享受破坏的过程吧!

优秀的个人博客,低调大师

源码资源、小程序、小游戏、网站、APP开发制作

你能做一名程序猿吗 我是一个从事互联网五年的程序员,在第三年的时候还做过一年的培训讲师(当时是培训机构的红利期),现在还是在做开发,大部分都是想通过培训转行的小白,但是又对IT行业充满迷惑,只知道好像工资很多,但是又不知道里面的细分职位,这也是行业互相之间存在着巨大的信息不对称。而一个小白如果想进入一个行业,如果不进行深入的调查,很容易被表面上你获取的信息所蒙蔽或者被相关利益方所误导,最后造成很多损失。 所以我觉得我的文章会帮助到很多小白~不懂就问! IT行业将在接下来很长时间里依然是促进社会发展的一个重要推动力之一。 因此必然将持续需要大量的人才,现在人才缺口依然很大,所以,如果你想转IT,目前依然机会合适。但是里面细分多少岗位呢? 简单来说一个产品的产生,是由老板说一个大方向,产品定下来主打方向,并且调研看是否可行,然后做出原型,UI根据原型出设计稿,前、后端程序员的技术分析产品文档,根据文档定接口,前端拿到UI的设计稿然后用代码实现,并且使用后端提供的数据接口,最后测试,运维上线,运营推广。我想大部分人,老板是当不了了,一般也都是做技术开发,这也是互联网里最多的职业,前后端开发。 其中前端就是写页面的,比如你能看到的网页/移动端网页/小程序/甚至于app,前端都可以做,前端是负责展示,而后端呢,就是对数据的操作。 我无数个亲戚说什么大数据年薪30W起步,我都觉得无语凝噎,大数据和ai需要对数学以及其他的很强的专业知识,并不是说仅仅会编程语言就行的,看到此贴的绝大部分人是不可能从事此行业的,就算你去培训这样的,也不过培训之后还是从事老本行,因为现在大数据和AI是普通人做不了的,就算我,一个从事互联网五年的老鸟,也做不了,而且我一本相关专业出身,干了五年多了,年薪才30W,而一个初出茅庐的新人,他想出来就是30W,没问题,那你有对应的知识和能力吗?大部分人是根本没有的!AI和大数据在互联网从业者里也是一小撮的。所以看了上面我说的,以及我的图后,就不要无脑的去培训java,好像互联网只有Java一样,java是国内互联网最多的职位,但是蛋糕再大,也架不住分的人多啊,现在国内java的现状就是企业有太多的选择了,人多嘛,不乏本专业出身的本科学历的,所以如果不是对后端数据特别感兴趣或者说本专业,我是不太赞同无脑培训java的,明显现在前端相对好点啊,毕竟5G对移动互联网又是一波节奏~如果是女孩子,推荐UI或者前端开发或者测试,这三者的岗位来说是前端>UI>测试,复杂度也是如此,鉴于女孩子对美的捕捉比较强以及其他方面综合考虑,还是最推荐女生学习UI。如果是有点基础/纯零基础的男生,最佳考虑前端,或者学习python做运维或者测试,只会Python是肯定找不到工作的,python是很好的辅助语言,但是只有他的话,无法Carry,但是很多人做前端薪资卡在15K,很多五年卡在20K,不过...好像大部分人来说15K或者20K就不错了。 个人企业商家需要做网站微信小程序 开发APP购买各种BBCSSC源代码 各种棋牌游戏源代码,手上有2018原草根吧整站资源,打包出售中。软文推广找云南雷厉科技有限公司。电话13658880498微信lonhjiale3 QQ708894317同步,注册账号,自已就能做模版搭建非常简单。 前端的发展就是不可以只做前端,你要涉及一些后端,并不是让你从事后端,而是你要在后期懂点后端,比方说现在随便拉一个五年以上程序员,哪个不是前后端都涉及?面就是去哪儿学,讲道理,培训班的手段套路,我门清儿~,而且现在培训机构市场杀成红海,都是争人头。这是一个很奇怪的行业,也是被互联网一直唾弃好像带有原罪的行业,可大多数人又不得不选择他作为前往互联网的跳板。 祝你早日成为一个程序猿

优秀的个人博客,低调大师

微信小游戏「跳一跳」外挂(java版)

image 其实也不能说算是外挂吧,算是个游戏小助手吧,毕竟不能抓包,也不能直接修改分数(据说之前可以直接抓包修改分数,不过这漏洞已经被微信官方修复),今天这个是 Android 同学可以非常容易看懂的一篇文章,是从 Android 的角度实现的,附带着技术原理分析和代码分析。 这个开源库已经被我同学分享到 GitHub 上,他自己很无聊,就写了这个东西和这篇文章,自己通过写代码实现高分也是玩的不亦乐乎,这就是程序员和普通玩家的区别吧。 开源库地址:https://github.com/xushanmeng/WechatJumpHelper 功能简介 用JAVA自动控制手机玩跳一跳 自动识别图像计算距离 自动帮你点击屏幕 自动缓存图片,并在图片上标记一些识别结果,如下图: image 运行环境 JAVA,最低版本为7.0,官网下载 adb驱动,官网下载(需要翻墙),或者到这里下载SDK-tools,其中就包含adb 安卓手机,目前已适配分辨率 1600x2560 1440x2560 1080x1920 720x1080 使用方法 有JAVA开发工具的同学可以直接运行java代码,便于代码调试,下面主要介绍运行已经打包好的jar包的方法 手机打开USB调试,并连接电脑 打开USB调试方法,进入设置,找到开发者选项,打开并勾选USB调试; 如果没有开发者选项,进入关于手机,连续点击版本号7次,即可开启开发者选项。 通过下面的命令,运行Android.jar java -jar Android.jar 根据手机分辨率选择跳跃系数,目前已适配机型: 其他分辨率请自己微调。 1600x2560机型推荐0.92 1440x2560机型推荐1.039 1080x1920机型推荐1.392 720x1080机型推荐2.078 原理说明 通过adb命令控制手机截图,并取回到本地 adb shell screencap -p /sdcard/screen.png adb pull /sdcard/screen.png . 图片分析 有靶点,即目标物体中心的白色圆点,则靶点中心为目标落点 无靶点,但是纯色平面,或者规则平面,则平面中心为目标落点 无靶点,又无纯色规则平面,但是左上和右上位置的斜率是固定的,可根据固定斜率的斜线和目标物体中心线的焦点计算落点 根据棋子的颜色,取顶部和底部的特征像素点,在截图中进行匹配,找到棋子坐标 由于目标物体不是在左上就是在右上,可以从上往下扫描,根据色差判断目标物体位置,其中又分为以下几种类型 image image image 计算棋子坐标和目标落点的距离 距离×跳跃系数=按压屏幕的时间,不同分辨率的手机,跳跃系数也有所不同 通过adb命令,给手机模拟按压事件 adb shell input swipe x y x y time 其中x和y是屏幕坐标,time是触摸时间,单位ms。 工程结构 image 代码详解 这里将针对一些关键算法的代码进行解释 寻找棋子位置 把截图放大,可以看到棋子顶部像素连成一条横线,那么我们通过颜色匹配,找到这一条线的始末位置,取中间位置,就得到了棋子的x坐标。 image 棋子的底部也是一条横线,用颜色匹配,我们检测到相似颜色的最大y坐标,就是棋子底部了,不过考虑到棋子底部是个圆盘,我们把棋子的y坐标再往上提一些。 image.gif 这样我们就得到了棋子的xy坐标,下面是相关代码: /* 计算棋子位置 */Pixel piece = new Pixel();for (int i = TOP_BORDER; i < screenHeight - BOTTOM_BORDER; i++) { int startX = 0; int endX = 0; for (int j = LEFT_BORDER; j < screenWidth - RIGHT_BORDER; j++) { int red = Color.red(pixels[i][j].color); int green = Color.green(pixels[i][j].color); int blue = Color.blue(pixels[i][j].color); if (50 < red && red < 55 && 50 < green && green < 55 && 55 < blue && blue < 65) {//棋子顶部颜色 //如果侦测到棋子相似颜色,记录下开始点 if (startX == 0) { startX = j; endX = 0; } } else if (endX == 0) { //记录下结束点 endX = j; if (endX - startX < PIECE_TOP_PIXELS) { //规避井盖的BUG,像素点不够长,则重新计算 startX = 0; endX = 0; } } if (50 < red && red < 60 && 55 < green && green < 65 && 95 < blue && blue < 105) {//棋子底部的颜色 //最后探测到的颜色就是棋子的底部像素 piece.y = i; } } if (startX != 0 && piece.x == 0) { piece.x = (startX + endX) / 2; }}//棋子纵坐标从底部边缘调整到底部中心piece.y -= PIECE_BOTTOM_CENTER_SHIFT; 寻找靶点 所谓靶点,就是目标物体中心的那个小圆点,颜色值为0xf5f5f5。 image 那么我们只需要寻找颜色值为0xf5f5f5的色块就可以了,为了规避其他物体相近颜色干扰,我们可以限制色块的大小,正确大小的色块才是靶点。 但是如何计算色块的大小呢,色块最顶端到最底端y坐标的差值我们作为色块的高度,同理,最左侧到最右侧x坐标的差值作为宽度,我们只需要查找这四个顶点的坐标就可以了。 本来打算用凸包的Graham扫描算法,后来发现色块已经是凸包了,且边缘像素是连续的,那么我们按照一定顺序,遍历边缘像素,就可以在O(n^-2)的时间复杂度里,得到色块的顶点坐标了。 我们从第一个像素点开始,寻找的顺序如图所示: image /** * 寻找色块顶点像素 */ public static final Pixel[] findVertexs(Pixel[][] pixels, Pixel firstPixcel) { Pixel[] vertexs = new Pixel[4]; Pixel topPixel = firstPixcel; Pixel leftPixel = firstPixcel; Pixel rightPixel = firstPixcel; Pixel bottomPixel = firstPixcel; Pixel currentPixcel = firstPixcel; //先把坐标置于左上角 while (checkBorder(pixels, currentPixcel)//判断是否超出图像边缘 && Color.compareColor(pixels[currentPixcel.y - 1][currentPixcel.x], firstPixcel)) {//判断是否是相同颜色 currentPixcel = pixels[currentPixcel.y - 1][currentPixcel.x]; } while (checkBorder(pixels, currentPixcel) && Color.compareColor(pixels[currentPixcel.y][currentPixcel.x - 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x - 1]; } //寻找上顶点像素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y - 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y - 1][currentPixcel.x]; } else if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x + 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x + 1]; } else { topPixel = findCenterPixcelHorizontal(pixels, currentPixcel); break; } } //寻找右顶点像素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x + 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x + 1]; } else if (Color.compareColor(pixels[currentPixcel.y + 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y + 1][currentPixcel.x]; } else { rightPixel = findCenterPixcelVertial(pixels, currentPixcel); break; } } //寻找下顶点像素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y + 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y + 1][currentPixcel.x]; } else if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x - 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x - 1]; } else { bottomPixel = findCenterPixcelHorizontal(pixels, currentPixcel); break; } } //寻找左顶点像素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x - 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x - 1]; } else if (Color.compareColor(pixels[currentPixcel.y - 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y - 1][currentPixcel.x]; } else { leftPixel = findCenterPixcelVertial(pixels, currentPixcel); break; } } vertexs[0] = leftPixel; vertexs[1] = topPixel; vertexs[2] = rightPixel; vertexs[3] = bottomPixel; return vertexs; } 得到了四个坐标点,我们就可以计算色块的中点了,也就是目标落点。 对于没有靶点,但是落点是规则平面的,也可以用类似算法。 斜率计算对于没有靶点,又不是规则平面的,我们怎么计算落点呢,这时候就要用到斜率了。 可以看得出来,每次左上角或右上角出现的物体,针对当前物体的方向都是一样的,也就是两个物体中心的连线,斜率是固定的。 基本所有的目标物体,最顶点像素中点的x坐标,都是在物体中间,我们至少先得到了目标物体x坐标了,记为des.x ,接下来要求des.y 。 image.gif 如上图所示,计算过程如下: 斜线的公式为 y=kx+b 那么,在棋子坐标上有 piece.y=k*piece.x+b 在目标落点坐标上有 des.y=kdes.x+b 代入得到 des.y=k*(des.x-piece.x)+piece.y 然而这种算法还是有偏差的。 image.gif 可以看到,同样的斜率,如果棋子的位置有偏差,计算出来最终落点还是会有偏差的。 代码解析就先讲这么多,希望有大神可以提出更好的解决方案。 玩游戏小窍门 连续的落到物体中心位置,是有分数加成的,最多跳一次可以得几十分 井盖、商店、唱片、魔方,多停留一会,有音乐响起后也是有分数加成的 那么看一下程序员的朋友圈有多残酷吧 image 原文作者:Samon阅读原文

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Oracle

Oracle

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse

Eclipse

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

JDK

JDK

JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。