嗟乎,忆昔大一时,初涉编程之境,撰此代码。时光荏苒,白驹过隙,转瞬已届大四之年。重览往日博客,见曾与网友言,期有闲暇优化此段程序,然岁月匆匆,迄今两年有余,竟未践此愿。
今幸保研已确,寒假将临,校中课程了无生趣。值此悠游之际,心有所感,思欲兑现昔日之诺,着手优化旧时代码,亦为自我精进之一途也。
何谓汉诺塔呢?
汉诺塔(Tower of Hanoi)是一个源于印度古老传说的益智玩具。有三根杆子(1 2 3),1杆上有若干个不同大小的圆盘按照从大到小的顺序堆叠。目标是把所有的圆盘移动到最后一根杆子上,但每次只能移动最上面的一个圆盘,并且任何时候都不能将较大的圆盘放在较小的圆盘之上。
(他奶奶的写了半天没保存,东西直接没了。。。)
咳咳,首先来看下最终效果,以确保这是大家想要的东西:
这是游戏的流程:
确定好流程,显然,我们需要一个记录每根柱子状态的结构体,为了图形化展示柱子及盘子的位置,需要一个二维数组作为画布。
首先,介绍一下两个可以增加交互体验的函数
#include<conio.h> getch()
直接从键盘获取键值,不等待用户按回车,只要用户按一个键,getch就立刻返回。
#include<windows.h> system("cls")
清屏函数
接下来就是一步步实现各个流程的功能了
一、全局定义常量和相关变量
1.定义了游戏的最大层数MAX_、画布的最大宽度WIDTH和最大高度HEIGHT等常量。
2.定义了全局变量如操作步数num、难度选择level、实际画布宽度width、高度height(根据选择的level确定)。
// 汉诺塔最大层数, 画布最大宽度,最大高度
const int MAX_ = 10;
const int WIDTH = MAX_ * 6 + 10, HEIGHT = MAX_ + 2;
//操作步数,难度选择,实际画布宽度,高度
int num, level, width, height;
二、结构体定义
1. 定义一个画布,存储当前平面视图,‘|’代表柱子,‘_’代表地面,‘*’代表盘子
2.定义柱子的结构体,记录每根柱子的状态
// 定义图画布-略大一点以防溢出
int graph[HEIGHT + 5][WIDTH + 5];
// 盘子移动相关
// 柱子结构体
struct Pillar {
int loc; // 水平位置
int num; // 盘子的数目
int plate[MAX_ + 5]; // 每个盘子的半径大小
} pillar[4];
pillar[4]创建了一个包含4个元素的Pillar结构体数组,虽然实际上只用到了索引1到3对应的柱子,第0个元素并未使用,这是为了使得柱子的索引与玩家输入的柱子编号相匹配(即用户输入1代表第一个柱子)。
需要注意的是,在后续的代码实现中,图形界面的底部(即地面)被放置在较高的行坐标位置,即graph[height]行,接着在graph[height-1]行 graph[height - 2]行...依次放置第一个第二个盘子(如果有)以此类推。
三、盘子移动相关函数
盘子从p柱子移动到q柱子,其实也就是p柱子减少一个盘子,q柱子增加一个盘子。这里定义给某个柱子增加盘子的函数add,及删减函数del
函数 add:
void add(int p, int l) {
pillar[p].plate[++pillar[p].num] = l;
int y = height - pillar[p].num;
for (int i = 1; i <= l; i++) {
graph[y][pillar[p].loc - i] = '*';
graph[y][pillar[p].loc + i] = '*';
}
}
·p:要添加盘子的柱子索引(1-3之间)。
·l:要添加的盘子的半径大小。
此函数首先增加柱子上盘子的数量计数器num,然后将新的盘子大小存入plate数组中。接着根据新加入的盘子数量更新其在图形界面中的垂直位置y,最后通过循环为这个新的盘子在图形界面上绘制星号*来表示盘子的存在。
函数 del:
void del(int p) {
if (!pillar[p].num) return;
int y = height - pillar[p].num;
for (int i = 1; i <= pillar[p].plate[pillar[p].num]; i++) {
graph[y][pillar[p].loc - i] = 0;
graph[y][pillar[p].loc + i] = 0;
}
pillar[p].num--;
}
p:要从中移除盘子的柱子索引(1-3之间)。
该函数检查柱子上是否有盘子,如果没有则直接返回。如果有,则找到顶部盘子的位置,并清除它在图形界面上的显示(用0代替星号*以清除, 也可以是符号空格' ')。之后减少柱子上盘子的数量计数器num。
根据汉诺塔规则,p到q移动不一定合法,再结合add和del函数实现p上的盘子移动到q的函数cal
int cal(int p, int q) {
if (p < 1 || p > 3 || q < 1 || q > 3) {
printf("非法操作:呜呜呜,我们的柱子序号只有1-3\n");
return 0;
}
if (p == q) {
printf("非法操作:如果我没有看错,这是同一根柱子,对吧。\n");
return 0;
}
if (!pillar[p].num) {
printf("非法操作:难道要我把空气移动上去?或许其他的游戏可以吧\n");
return 0;
}
if (pillar[q].num &&
pillar[p].plate[pillar[p].num] > pillar[q].plate[pillar[q].num]) {
printf("非法操作:大的圆盘将压到小的圆盘\n");
return 0;
}
add(q, pillar[p].plate[pillar[p].num]);
del(p);
num++;
return 1;
}
·p:来源柱子索引。
·q:目标柱子索引。
cal函数负责验证和执行从柱子p到柱子q的移动。它会检查以下几种非法情况:
1.移动超出范围(柱子编号不在1-3之间)。
2.来源柱子和目标柱子相同。
3.来源柱子上没有盘子可移。
4.将较大的盘子放置在较小的盘子之上。
如果所有检查都通过,它就会调用add函数将盘子添加到目标柱子,并调用del函数从来源柱子删除盘子。成功后,步数计数器num加一,并返回1表示成功;若任一检查失败,则打印相应的错误信息并返回0表示失败。
四、界面打印相关函数:
实现汉诺塔游戏的界面部分,包括打印中心化的字符串、显示起始界面、提供操作说明以及渲染当前的游戏状态(即画布)。下面是对每个函数的具体介绍和思路分析:
print_centered(const char* str):以居中方式打印字符串。但其实用的不多,直接printf也差不多,或许我以后有了更好的优化思路就能用上了。
// 打印中心化字符串
void print_centered(const char* str) {
int padding = (WIDTH - strlen(str)) / 2;
for (int i = 0; i < padding; ++i) printf(" ");
printf("%s\n", str);
}
·计算出需要填充的空格数量(padding),使得字符串能够位于屏幕宽度(WIDTH)的中间位置。
·使用循环打印空白字符来创建左侧的填充空间。
·最后打印传入的字符串并换行。
print_start():打印游戏开始界面
展示游戏的起始界面,并等待玩家按下任意键以开始游戏。
// 打印起始界面
void print_start() {
system("cls");
print_centered("########################################");
print_centered("########################################");
print_centered("########################################");
print_centered("##### 汉诺塔 #####");
print_centered("########################################");
print_centered("########################################");
print_centered("#### 按任意键开始 ####################");
print_centered("########################################");
print_centered("########################################");
getch();
}
·首先清屏(system("cls")),确保界面整洁。
·利用print_centered函数多次调用来构造一个美观且对称的起始画面。
·最后调用getch()暂停程序执行,直到用户按下任意键,给予玩家时间阅读并准备好开始游戏。
print_instruction():打印游戏操作说明。
// 打印操作说明
void print_instruction() {
system("cls");
print_centered(" 游戏玩法");
printf(" 1.首先靠按键 A D 或者 方向键 移动选择初始汉诺塔层数,回车确认\n");
printf(" 2.使用数字键对塔上圆盘进行移动,例如连续输入 1 3 "
"即表示将第1根柱子最上面的圆盘移动到第三根上\n");
printf(" 3.无法移动处于第一个以下的圆盘,大的圆盘不能在小的上面\n");
printf(" 若知晓,按任意键即可开始游戏\n");
getch();
}
向玩家展示游戏操作指南,帮助他们了解如何玩这个游戏。
print_graph():打印当前的游戏状态(即图形化显示汉诺塔)。
比较简单,for循环实现即可。
// 打印画布
void print_graph() {
system("cls");
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++) {
if (graph[i][j] == 0) {
printf(" ");
} else {
printf("%c", graph[i][j]);
}
}
printf("\n");
}
}
五、游戏逻辑相关函数
choose_level():让用户选择游戏难度(即汉诺塔的层数)。
还挺长的,主要是实现括号移动的效果
// 选择难度
int choose_level() {
int ceng = 1, c;
while (1) {
system("cls");
print_centered("***********************");
print_centered("**** 选择层数 ****");
print_centered("***********************");
printf("\n ");
for (int i = 1; i <= MAX_; ++i) {
if (i == ceng) {
printf(" <%d> ", i);
} else {
printf(" %d ", i);
}
}
c = getch();
if (c == 'd' || c == 'D') {
ceng = (ceng % MAX_) + 1; // 循环增加层数
} else if (c == 'a' || c == 'A') {
ceng--; // 循环减少层数
if(ceng <= 0) ceng = MAX_;
} else if (c == 0 || c == 224) {
c = getch(); // 获取方向键的具体码
switch (c) {
case 72: // 上箭头
ceng = (ceng % MAX_) + 1; // 循环增加位置
break;
case 80: // 下箭头
ceng--; // 循环减少层数
if(ceng <= 0) ceng = MAX_;
break;
case 75: // 左箭头
ceng--; // 循环减少层数
if(ceng <= 0) ceng = MAX_;
break;
case 77: // 右箭头
ceng = (ceng % MAX_) + 1; // 循环增加层数
break;
}
}
// 确认
if (c == 13) {
system("cls");
return ceng;
}
}
}
·初始化与循环:函数开始时设置默认层数为1(即最简单的情况)。然后进入一个无限循环,直到玩家按下回车键确认选择。
·界面更新:每次循环迭代中,先清屏,然后使用 print_centered 函数打印出标题和当前可以选择的所有层数。当前选中的层数会被高亮显示(例如用尖括号包围)。
·用户输入处理:监听用户的按键输入。如果用户按下了 A 或 D 键(或左右方向键),则根据按键调整当前选中的层数。当用户按下回车键时,退出循环并返回所选层数。
·边界检查:确保层数不会超出预定义的最大值 MAX_。如果减法操作导致层数小于等于0,则将其重置为最大值;同样地,增加层数超过最大值时也应循环回到最小值。
init():负责初始化游戏的基本参数,包括难度级别、画布尺寸以及每个柱子的位置和初始状态。
// 初始化难度,宽度,高度
void init() {
level = choose_level();
width = level * 6 + 10, height = level + 2;
memset(pillar, 0, sizeof(pillar));
pillar[1].loc = level + 2;
pillar[2].loc = pillar[1].loc + level * 2 + 2;
pillar[3].loc = pillar[2].loc + level * 2 + 2;
num = 0;
// 初始化画布
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++) {
graph[i][j] = 0;
}
for (int j = 1; j <= 3; ++j) {
graph[i][pillar[j].loc] = '|';
}
}
for (int i = 1; i <= width; i++) {
graph[height][i] = '_';
}
for (int i = level; i; --i) {
add(1, i);
}
}
·调用 choose_level:首先调用 choose_level 来获取用户选定的游戏难度(层数)。
·计算尺寸:根据选定的层数计算画布的宽度和高度。宽度是基于层数乘以固定的比例加上额外的空间,高度则是层数加一个小常数。
·初始化数据结构:将所有柱子的数据结构(如位置、盘子数量等)初始化为空。同时确定三个柱子在画布上的水平位置。
·设置画布:遍历整个 graph 数组,将每个元素设为空(即没有绘制任何东西)。接着在适当的位置放置竖线字符 | 表示柱子,并在底部一行全部填充下划线 _ 作为地面。
·添加初始盘子:按照选定的层数,依次调用 add 函数向第一个柱子添加相应大小的盘子,从最大的开始。
try_again():询问玩家是否想要重新开始游戏。
// 是否继续
int try_again() {
printf("是否重新开始游戏?\n");
printf("y.是\n");
printf("n.否\n");
int c = getch();
if (c == 'y' || c == 'Y') {
return 1;
} else {
return 0;
}
}
·打印提示信息给玩家,提供两个选项——继续游戏('y'/'Y')或者结束程序(其他任意键)。
·根据玩家的按键输入决定返回 1(表示继续)或 0(表示退出)。
playGame():主游戏循环,负责处理用户输入、更新游戏状态直到游戏结束。
// 正式开始一次游戏
int playGame() {
print_start();
print_instruction();
init();
while (pillar[3].num != level) {
print_graph();
int p = 0, q = 0;
printf("请输入要移动的柱子:");
char c = getch();
while (!isdigit(c)) c = getch();
printf("%c\n", c);
p = c - '0';
printf("请输入移动到的柱子:");
c = getch();
while (!isdigit(c)) c = getch();
printf("%c\n", c);
q = c - '0';
if (!cal(p, q)) {
getch();
}
}
print_graph();
printf("恭喜你,你赢了!\n");
printf("你用了%d步,总共有%d层\n", num, level);
printf("按任意键退出游戏\n");
getch();
return try_again();
}
·准备阶段:调用 print_start 和 print_instruction 显示欢迎界面和操作指南,随后调用 init 准备好游戏环境。
·游戏进行中:进入一个循环,只要第三个柱子上还没有达到指定数量的盘子,就持续进行游戏。在这个循环里:
·使用 print_graph 函数刷新屏幕,展示当前的游戏状态。
·提示玩家输入要移动的源柱子编号和目标柱子编号。
·验证玩家输入的有效性,并调用 cal 函数尝试执行合法的移动。
·如果玩家的操作非法,则给予反馈并等待玩家按键继续。
·胜利检测:一旦第三个柱子上有足够的盘子,就认为玩家赢得了游戏。此时打印祝贺信息,显示玩家完成游戏所需的步数,并调用 try_again 确认玩家是否想再玩一次。
六、主函数 (main)
游戏入口点,通过调用playGame()来启动游戏,并根据玩家的选择决定是否重新开始游戏。
int main() {
while (playGame()) {}
return 0;
}
七、全部代码
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
// 汉诺塔最大层数, 画布最大宽度,最大高度
const int MAX_ = 10;
const int WIDTH = MAX_ * 6 + 10, HEIGHT = MAX_ + 2;
// 定义图画布-略大一点以防溢出
int graph[HEIGHT + 5][WIDTH + 5];
//操作步数,难度选择,实际画布宽度,高度
int num, level, width, height;
// 盘子移动相关
// 柱子结构体
struct Pillar {
int loc; // 水平位置
int num; // 盘子的数目
int plate[MAX_ + 5]; // 每个盘子的半径大小
} pillar[4];
void add(int p, int l) {
pillar[p].plate[++pillar[p].num] = l;
int y = height - pillar[p].num;
for (int i = 1; i <= l; i++) {
graph[y][pillar[p].loc - i] = '*';
graph[y][pillar[p].loc + i] = '*';
}
}
void del(int p) {
if (!pillar[p].num) return;
int y = height - pillar[p].num;
for (int i = 1; i <= pillar[p].plate[pillar[p].num]; i++) {
graph[y][pillar[p].loc - i] = 0;
graph[y][pillar[p].loc + i] = 0;
}
pillar[p].num--;
}
int cal(int p, int q) {
if (p < 1 || p > 3 || q < 1 || q > 3) {
printf("非法操作:呜呜呜,我们的柱子序号只有1-3\n");
return 0;
}
if (p == q) {
printf("非法操作:如果我没有看错,这是同一根柱子,对吧。\n");
return 0;
}
if (!pillar[p].num) {
printf("非法操作:难道要我把空气移动上去?或许其他的游戏可以吧\n");
return 0;
}
if (pillar[q].num &&
pillar[p].plate[pillar[p].num] > pillar[q].plate[pillar[q].num]) {
printf("非法操作:大的圆盘将压到小的圆盘\n");
return 0;
}
add(q, pillar[p].plate[pillar[p].num]);
del(p);
num++;
return 1;
}
// 打印界面相关
// 打印中心化字符串
void print_centered(const char* str) {
int padding = (WIDTH - strlen(str)) / 2;
for (int i = 0; i < padding; ++i) printf(" ");
printf("%s\n", str);
}
// 打印起始界面
void print_start() {
system("cls");
print_centered("########################################");
print_centered("########################################");
print_centered("########################################");
print_centered("##### 汉诺塔 #####");
print_centered("########################################");
print_centered("########################################");
print_centered("#### 按任意键开始 ####################");
print_centered("########################################");
print_centered("########################################");
getch();
}
// 打印操作说明
void print_instruction() {
system("cls");
print_centered(" 游戏玩法");
printf(" 1.首先靠按键 A D 或者 方向键 移动选择初始汉诺塔层数,回车确认\n");
printf(" 2.使用数字键对塔上圆盘进行移动,例如连续输入 1 3 "
"即表示将第1根柱子最上面的圆盘移动到第三根上\n");
printf(" 3.无法移动处于第一个以下的圆盘,大的圆盘不能在小的上面\n");
printf(" 若知晓,按任意键即可开始游戏\n");
getch();
}
// 打印画布
void print_graph() {
system("cls");
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++) {
if (graph[i][j] == 0) {
printf(" ");
} else {
printf("%c", graph[i][j]);
}
}
printf("\n");
}
}
// 游戏相关
// 选择难度
int choose_level() {
int ceng = 1, c;
while (1) {
system("cls");
print_centered("***********************");
print_centered("**** 选择层数 ****");
print_centered("***********************");
printf("\n ");
for (int i = 1; i <= MAX_; ++i) {
if (i == ceng) {
printf(" <%d> ", i);
} else {
printf(" %d ", i);
}
}
c = getch();
if (c == 'd' || c == 'D') {
ceng = (ceng % MAX_) + 1; // 循环增加层数
} else if (c == 'a' || c == 'A') {
ceng--; // 循环减少层数
if(ceng <= 0) ceng = MAX_;
} else if (c == 0 || c == 224) {
c = getch(); // 获取方向键的具体码
switch (c) {
case 72: // 上箭头
ceng = (ceng % MAX_) + 1; // 循环增加位置
break;
case 80: // 下箭头
ceng--; // 循环减少层数
if(ceng <= 0) ceng = MAX_;
break;
case 75: // 左箭头
ceng--; // 循环减少层数
if(ceng <= 0) ceng = MAX_;
break;
case 77: // 右箭头
ceng = (ceng % MAX_) + 1; // 循环增加层数
break;
}
}
// 确认
if (c == 13) {
system("cls");
return ceng;
}
}
}
// 初始化难度,宽度,高度
void init() {
level = choose_level();
width = level * 6 + 10, height = level + 2;
memset(pillar, 0, sizeof(pillar));
pillar[1].loc = level + 2;
pillar[2].loc = pillar[1].loc + level * 2 + 2;
pillar[3].loc = pillar[2].loc + level * 2 + 2;
num = 0;
// 初始化画布
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++) {
graph[i][j] = 0;
}
for (int j = 1; j <= 3; ++j) {
graph[i][pillar[j].loc] = '|';
}
}
for (int i = 1; i <= width; i++) {
graph[height][i] = '_';
}
for (int i = level; i; --i) {
add(1, i);
}
}
// 是否继续
int try_again() {
printf("是否重新开始游戏?\n");
printf("y.是\n");
printf("n.否\n");
int c = getch();
if (c == 'y' || c == 'Y') {
return 1;
} else {
return 0;
}
}
// 正式开始一次游戏
int playGame() {
print_start();
print_instruction();
init();
while (pillar[3].num != level) {
print_graph();
int p = 0, q = 0;
printf("请输入要移动的柱子:");
char c = getch();
while (!isdigit(c)) c = getch();
printf("%c\n", c);
p = c - '0';
printf("请输入移动到的柱子:");
c = getch();
while (!isdigit(c)) c = getch();
printf("%c\n", c);
q = c - '0';
if (!cal(p, q)) {
getch();
}
}
print_graph();
printf("恭喜你,你赢了!\n");
printf("你用了%d步,总共有%d层\n", num, level);
printf("按任意键退出游戏\n");
getch();
return try_again();
}
int main() {
while (playGame()) {}
return 0;
}
emmmmm,感觉至少比之前的强一点了,可喜可贺,可喜可贺啊
之前的博客:
相信大家在学习C语言嵌套的时候已经学习过经典问题汉诺塔了,我学习时就想能不能搞个汉诺塔的游戏出来,结果睡了一觉就给忘了。今天突然想起,搞出来与大家分享,水平一般,但符合汉诺塔规则。最终代码我放在文章最后,希望能对大家的学习有一些帮助。
重要函数
除了stdio.h中的函数以外,我们还需要用到一些其它的库函数增加游戏体验。
#include<conio.h> getch()
直接从键盘获取键值,不等待用户按回车,只要用户按一个键,getch就立刻返回。
#include<windows.h> system("cls")
清屏函数
打印界面
最简单的一步,printf,不需要多赘述,打印完后清屏,输出操作方法与玩法.
printf(" ########################################\n");
printf(" ########################################\n");
printf(" ##### 汉诺塔 #####\n");
printf(" ########################################\n");
printf(" ########################################\n");
printf(" #### 按任意键开始 ####################\n");
printf(" ########################################\n");
printf(" ########################################\n");
getch();
system("cls");
//输出操作方法
printf(" 游戏玩法\n");
printf(" 1.首先靠按键 A D 移动选择初始汉诺塔层数,回车确认\n");
printf(" 2.使用数字键对塔上圆盘进行移动,例如连续输入 1 3 即表示将第1根柱子最上面的圆盘移动到第三根上\n");
printf(" 3.无法移动处于第一个以下的圆盘,大的圆盘不能在小的上面\n");
printf(" 若知晓,按任意键即可开始游戏\n");
getch();
system("cls");
添加必要参数
int i,ceng = 1; /* i负责控制循环,ceng用来记录选择的层数*/
char c; /*得到从键盘输入的值*/
int wei1,wei2,bushu = 0; /*记录输入的第一根柱子,第二根柱子,已经走过的步数*/
int li[4]; /*立柱1 2 3 上的圆盘的个数*/
int zhu[4][6] = {0}; /*记录每个柱上每层圆盘的大小*/
li[2] = li[3] = 0;
选择难度,汉诺塔层数 (目前仅供选择 1 3 5 )
在得到每个合法输入之后进行对变量值的调整,清屏后重新输入。
while(1)
{printf(" ***********************\n");
printf(" **** 选择层数 ****\n");
printf(" ***********************\n");
printf("\n ");
if(ceng == 1)
printf("||1层|| ");
else
printf(" 1层 ");
if(ceng == 3)
printf("||3层|| ");
else
printf(" 3层 ");
if(ceng == 5)
printf("||5层|| ");
else
printf(" 5层 ");
c = getch();
if(c == 'd'||c == 'D')
{if( ceng == 5 )
ceng = 1;
else if( ceng == 3 )
ceng = 5;
else
ceng = 3;
}
if( c == 'a' || c == 'a' )
{if( ceng == 1 )
ceng = 5;
else if( ceng == 5 )
ceng = 3;
else
ceng = 1;
}
system("cls");
if( c == 13 )
break;
}
初始画面打印 (这里以横坐标7 9 31 为立柱的位置)
int hang,lie;
li[1] = ceng ;
for(i = 0;i < ceng; i++)
zhu[1][i] = ceng - i;
for( hang = 4 ;hang >= 0 ; hang-- )
{if( hang != 4 )
printf("\n");
for( lie = 0 ;lie <= 40 ;lie++ )
{if( lie == 7 || lie == 19 || lie == 31 )
printf("|");
else if( hang < li[1] && abs( lie - 7 ) <= zhu[1][hang] )
printf("*");
else
printf(" ");
}
}
printf("\n");
for(lie=0;lie<=40;lie++)
printf("-");
printf("\n");
printf("%d %d %d\n",li[1],li[2],li[3]);
printf("%d %d %d\n",zhu[1][ li[1] - 1 ],zhu[2][ li[2] - 1 ],zhu[3][ li[3] - 1 ]);
汉诺塔移动(游戏核心)
while(1)
{if( li[3] == ceng )
break;
wei1 = getch() - '0';
printf("输入柱1:%d号柱子\n",wei1);
wei2 = getch() - '0';
printf("输入柱2:%d号柱子\n",wei2);
Sleep(800);
if( 0 < wei1 && wei1 < 4 && 0 < wei2 && wei2 < 4 )
{if( ( zhu[wei1][ li[wei1] - 1 ] < zhu[wei2][ li[wei2] - 1 ] || zhu[wei2][ li[wei2] - 1 ] == 0 ) && zhu[wei1][ li[wei1] - 1 ] !=0 )
{int t; /*数据转换*/
t = zhu[wei1][ li[wei1] - 1 ];
zhu[wei1][ li[wei1] - 1 ] = 0;
zhu[wei2][ li[wei2] ] = t;
li[wei1]--;
li[wei2]++;
system("cls");
for( hang = 4 ;hang >= 0 ; hang-- )
{if( hang != 4 )
printf("\n");
for( lie = 0 ;lie <= 40 ;lie++ )
{if( lie == 7 || lie == 19 || lie == 31 )
printf("|");
else if( hang < li[1] && abs( lie - 7 ) <= zhu[1][hang] )
printf("*");
else if( hang < li[2] && abs( lie - 19 ) <= zhu[2][hang] )
printf("*");
else if( hang < li[3] && abs( lie - 31 ) <= zhu[3][hang] )
printf("*");
else
printf(" ");
}
}
printf("\n");
for(lie=0;lie<=40;lie++)
printf("-");
printf("\n");
bushu++;
printf("%d %d %d\n",li[1],li[2],li[3]);
printf("%d %d %d 总步数:%d\n",zhu[1][ li[1] - 1 ],zhu[2][ li[2] - 1 ],zhu[3][ li[3] - 1 ],bushu);
}
else if( wei1 == wei2 )
printf("违法操作:如果我没有看错,这是同一根柱子,对吧。\n");
else if( zhu[wei1][ li[wei1] - 1 ] > zhu[wei2][ li[wei2] - 1 ] )
printf("违法操作:大的圆盘将压到小的圆盘\n");
else if( zhu[wei1][ li[wei1] - 1 ] ==0 )
printf("违法操作:难道要我把空气移动上去?或许其他的游戏可以吧\n");
}
else
printf("违法操作:我们可不存在这样的柱子\n");
}
printf("你赢了\n");
善后处理
游戏本身不存在输的结果,在跳出循环之后,即可恭喜玩家获得胜利。再结合goto使游戏就可以循环着玩了。如果说如何退出,emmm,右上角的叉叉值得托付。
游戏的最终代码
#include<stdio.h>
#include<conio.h>
#include<windows.h>
int main(){
int i,ceng = 1;
char c;
int wei1,wei2,bushu = 0;
/*立柱位置7 19 31 */
int li[4];
int zhu[4][6] = {0};
li[2] = li[3] = 0;
/*打印界面*/
KAIDUAN:
system("cls");
printf(" ########################################\n");
printf(" ########################################\n");
printf(" ##### 汉诺塔 #####\n");
printf(" ########################################\n");
printf(" ########################################\n");
printf(" #### 按任意键开始 ####################\n");
printf(" ########################################\n");
printf(" ########################################\n");
getch();
system("cls");
//输出操作方法
printf(" 游戏玩法\n");
printf(" 1.首先靠按键 A D 移动选择初始汉诺塔层数,回车确认\n");
printf(" 2.使用数字键对塔上圆盘进行移动,例如连续输入 1 3 即表示将第1根柱子最上面的圆盘移动到第三根上\n");
printf(" 3.无法移动处于第一个以下的圆盘,大的圆盘不能在小的上面\n");
printf(" 若知晓,按任意键即可开始游戏\n");
getch();
system("cls");
/*选择难度,汉诺塔层数*/
while(1)
{printf(" ***********************\n");
printf(" **** 选择层数 ****\n");
printf(" ***********************\n");
printf("\n ");
if(ceng == 1)
printf("||1层|| ");
else
printf(" 1层 ");
if(ceng == 3)
printf("||3层|| ");
else
printf(" 3层 ");
if(ceng == 5)
printf("||5层|| ");
else
printf(" 5层 ");
c = getch();
if(c == 'd'||c == 'D')
{if( ceng == 5 )
ceng = 1;
else if( ceng == 3 )
ceng = 5;
else
ceng = 3;
}
if( c == 'a' || c == 'a' )
{if( ceng == 1 )
ceng = 5;
else if( ceng == 5 )
ceng = 3;
else
ceng = 1;
}
system("cls");
if( c == 13 )
break;
}
/*处理初始地图*/
int hang,lie;
li[1] = ceng ;
for(i = 0;i < ceng; i++)
zhu[1][i] = ceng - i;
for( hang = 4 ;hang >= 0 ; hang-- )
{if( hang != 4 )
printf("\n");
for( lie = 0 ;lie <= 40 ;lie++ )
{if( lie == 7 || lie == 19 || lie == 31 )
printf("|");
else if( hang < li[1] && abs( lie - 7 ) <= zhu[1][hang] )
printf("*");
else
printf(" ");
}
}
printf("\n");
for(lie=0;lie<=40;lie++)
printf("-");
printf("\n");
printf("%d %d %d\n",li[1],li[2],li[3]);
printf("%d %d %d\n",zhu[1][ li[1] - 1 ],zhu[2][ li[2] - 1 ],zhu[3][ li[3] - 1 ]);
/*汉诺塔移动*/
while(1)
{if( li[3] == ceng )
break;
wei1 = getch() - '0';
printf("输入柱1:%d号柱子\n",wei1);
wei2 = getch() - '0';
printf("输入柱2:%d号柱子\n",wei2);
Sleep(800);
if( 0 < wei1 && wei1 < 4 && 0 < wei2 && wei2 < 4 )
{if( ( zhu[wei1][ li[wei1] - 1 ] < zhu[wei2][ li[wei2] - 1 ] || zhu[wei2][ li[wei2] - 1 ] == 0 ) && zhu[wei1][ li[wei1] - 1 ] !=0 )
{int t; /*数据转换*/
t = zhu[wei1][ li[wei1] - 1 ];
zhu[wei1][ li[wei1] - 1 ] = 0;
zhu[wei2][ li[wei2] ] = t;
li[wei1]--;
li[wei2]++;
system("cls");
for( hang = 4 ;hang >= 0 ; hang-- )
{if( hang != 4 )
printf("\n");
for( lie = 0 ;lie <= 40 ;lie++ )
{if( lie == 7 || lie == 19 || lie == 31 )
printf("|");
else if( hang < li[1] && abs( lie - 7 ) <= zhu[1][hang] )
printf("*");
else if( hang < li[2] && abs( lie - 19 ) <= zhu[2][hang] )
printf("*");
else if( hang < li[3] && abs( lie - 31 ) <= zhu[3][hang] )
printf("*");
else
printf(" ");
}
}
printf("\n");
for(lie=0;lie<=40;lie++)
printf("-");
printf("\n");
bushu++;
printf("%d %d %d\n",li[1],li[2],li[3]);
printf("%d %d %d 总步数:%d\n",zhu[1][ li[1] - 1 ],zhu[2][ li[2] - 1 ],zhu[3][ li[3] - 1 ],bushu);
}
else if( wei1 == wei2 )
printf("违法操作:如果我没有看错,这是同一根柱子,对吧。\n");
else if( zhu[wei1][ li[wei1] - 1 ] > zhu[wei2][ li[wei2] - 1 ] )
printf("违法操作:大的圆盘将压到小的圆盘\n");
else if( zhu[wei1][ li[wei1] - 1 ] ==0 )
printf("违法操作:难道要我把空气移动上去?或许其他的游戏可以吧\n");
}
else
printf("违法操作:我们可不存在这样的柱子\n");
}
printf("你赢了\n");
/*重新开始*/
for( hang = 3 ;hang >= 0 ;hang--)
for( lie = 0 ;lie <= 5 ;lie++)
zhu[hang][lie] = 0;
bushu = li[3] = 0;
Sleep(800);
printf("按任意键重新开始\n");
getch();
goto KAIDUAN;
return 0;
}