汉诺塔小游戏-c语言实现

嗟乎,忆昔大一时,初涉编程之境,撰此代码。时光荏苒,白驹过隙,转瞬已届大四之年。重览往日博客,见曾与网友言,期有闲暇优化此段程序,然岁月匆匆,迄今两年有余,竟未践此愿。

今幸保研已确,寒假将临,校中课程了无生趣。值此悠游之际,心有所感,思欲兑现昔日之诺,着手优化旧时代码,亦为自我精进之一途也。

何谓汉诺塔呢?
        汉诺塔(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;
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值