[音乐] 接下来我们来 看一个比较有趣的例子吧,例 20 龟兔赛跑。 那么再稍微放大一下, 那么在这样的一个龟兔赛跑的这样的一个模拟的例子里面,我们要 程序模拟一下龟兔赛跑的这样的寓言故事。 那么我们用这样的一个随机数产生器来这样的一个 建立模拟龟兔赛跑,其实这也是一个,相当于我们平时玩的这样的一个投掷的这样的一个投掷- 这样的一个 游戏。 那么选手呢从这样的第一格 开始起跑,每格表示跑到上面一个可能的位置,终点线在第 70 格地方。 第一个到达的选手将会有一些奖励,那么兔子有可能会在山坡上睡觉,因此可能也会输给乌龟。 那么每隔一秒钟的时间怎么样呢,我们更新一下这样的一个选手的这样的一个位置。 那么每一个选手的位置从 1 开始,那么如果选手这样的一个回退到第 1 格以外的话, 那么就直接移回到第 1 格,我们不允许它这样的一个回到第 1 格以外。 那么在这个乌龟的这样的一个概率里面,它有 50% 的可能性 可以依次向右移动 3 格,20% 的可能性向左移动 6 格,以及 30%的可能性 向右移动 1 格,这底下实际上是一个兔子的这样的概率。 所有的概率加起来刚好是一个 100%。 那么我们可以产生这样的一个 0 到 9 之间的这样的一个随机数, 那么当然这样的一个随机数如果是 0、 1、 2、 3、 4 这样五种可能性的话,我们就认为这样就有 50% 的可能性。 可以向右这样的 3 格,对吧?如果是 6,7 这样的 20% 的 可能性,那么 7、 8、 9 那么就是 30% 的这样的一个可能性。 我们通过这样的,产生这样的一个随机数来怎么样呢?来模拟这样的一个概率的这样的一个 跑步的一个方法。 那么起跑的时候我们会显示这样的一些 信息。 那么时钟每滴答一下,也就是说每这么过 1 秒的话 那么会显示这样的一个,更新一下这样的位置。 那么在更新位置的时候,如果两个位置重叠了,那么兔子就会 咬乌龟,那么程序将从该位置开始显示 OUCH!!! OUCH。 那么除了显示 T 跟 H 之外, 那么如果没有再咬的话,那么就在乌龟的位置上显示一个 T,在这样的一个 兔子的位置上显示这样的一个 H。 那么在 每一次之后,我们会测试这样的一个选手是不是超过了第 70 个,如果是的话就显示获胜。 由于这样的两个选手其实是分别开始投这样,投里面跑的。 所以说其实有可能在每一轮的时候怎么样呢?它们同时冲过了第 70 个,这是有可能发生的。 那么如果这样的话,我们,像我们在这个游戏规则里面,我们让 这个乌龟赢,显示这样的一个乌龟赢。 那么你也可以显示这样的一个平手,那么继续这样一个 继续这样的一个游戏。 好了,我们来看,这个 该怎么模拟它。 我们新建一个这样的空白程序, [无声] 看一下我们第 20 个例子。 [无声] 这个例子比较长 简单地就,只做一个复试吧,叫做例 20 龟兔 赛跑, 好了,那么在这样的一个例子里面,我们首先怎么样呢?我们首先创建一个 首先创建一个这样的一个乌龟类, [无声] 创建这样的一个乌龟类, 那么我们在乌龟类里面应该有一些什么样的一些东西呢?当然我们把这个乌龟类前面并没有写 public,我们仅仅只是在我们这里面使用这样的乌龟类,我们不会把它共享到其他地方- 去使用,所以说我们前面没有 public。 private/ 那么首先,我们能够想到的我们一定有 一个这样的一个乌龟的这样的一个位置,那么这样的一个位置它应该是 t 等于 1 的初值,在这里我们看到了 看到了一个新的写法,就是什么呢?我们既可以直接地声明一个字段的时候,或者变量的时候, 直接给它赋一个初值,而不一定非要把它放到 构造函数里面去给它赋初值。 那么可能你们会要问,如果两个地方都赋初值了,会发生什么样的情况呢? 那么情况是构造函数的这样的一个初值在后面,那么它会冲到前面的这样的一个初值。 private int 这样的一个 t 等于1, t 等于 1了之后表示这样的一个乌龟的内部的位置。 我们把它声明成这样的一个 private 类型的这样的一个变量。 我们其实要做的一个事情就是不断地要计算这个位置, 以及要更新这样的一个位置,那么为了计算这样的,更新这样的一个位置 其实我们还需要一个 Random 型的这样的一个随机数发生器,我们一定要更新这样的一个位置。 那么完了之后,我们基本上就是需要这两个东西就可以了。 完了之后我们 public,我们提供这样的一个构造函数, 在这个构造函数里面, 把这个构造函数里面 由于我们已经把t上面初始化为 1 了,所以说 这个构造函数里面,我们只是简单地产生一个新的这样的一个 Random 的对象。 当然了,这个产生的新的 Rondom 对象也可以直接写到这儿。 也可以直接写到这儿,这时我这个构造函数就基本上没有什么 必要再提供,因为我在上面就直接把这两个变量都已经初始化了。 当然了,我个人觉得自己提供一个 显示的构造函数还是一个比较好的这样的一个编程的习惯吧。 那么新建了这样的一个 Rondom 这样的构造函数了之后,那么iii最主要的一个 函数的问题我认为就应该是提供一个 public 的 函数,它返回一个整型的值,那么 Get 怎么样呢?得到当前的一个 position。 [无声] 这是我们最主要的这样的一个值。 那么在得到这个当前的 position 这个里面,应该怎么样做呢?它当然是根据现在的这个 t 来更新怎么样呢?下一个 t 的位置。 那么根据我们的这样的一个规则, 我们首先产生一个 0 到 9 之间的随机数, int randNumber 应该等于一个 r. 我们调用这个 random 对象的 Next 函数,给它一个参数 10, 表示产生一个 0 到 1 0 之间但不包括 10 但是却是包括 0 的这样的一个随机的整数。 那么有了这样的一个随机数了之后, switch, 我们来检查一下这样的一个随机数, switch (randNum) 检查这个随机数的时候我们判断一下怎么样,我们通过一个开关 语句,case 如果是 0 的话, case 如果是 1 的话, case 如果是 2 的话, case 如果是 3 的话, case 如果是 4 的话,如果是这五种可能性的话,那么我们来 做一些什么样的事情呢?那么顺便在这里边提一下 switch 语句 switch 需要注意,那么可能有写这样的一个来听课的这样的一个同学们他们 可能有过 C++ 的背景,或者 C 的背景,那么我们知道在 C 的时候, case 后面怎么样呢?可以带 break,也可以不带有 break。 CSHARP 这一点稍微跟它们有些不一样,最下部 规定每一个 case 后面都必须要跟随一个 break, 只要一种情况是例外,就是 case 后面任何语句的时候可以没有 break。 也就是这种情况下,可以把多个 case 怎么样呢?累计到一块写。 否则每一个 case 后面一旦有某个语句的话,它必须得有一个 break。 不能没有 break,这个要稍微注意一下 那么在这样的一个情况下,我们根据这样的 一个前面幻灯片提到的规则,我们再来看一下规则 规则是乌龟在百分之五十的情况下怎么样呢? 会向右移动三格,所以我们要 t 等于 t 加 3 向右移动三格 那么,剩下的 case 5 case 6。 在这样的两种情况下我们看一下 在百分之二十的的情况下,怎么样呢,向左六格,剩下的百分之三十的情况下向右一格 那么向左六格,那么 t 就应该 等于 t 减去 6 同时我们也注意到,在规则里面说了,它不能移成 0 或者 0 以后的那个数,它最多只能是到减到 1 所以说我们要判断 t 减 6 是不是大于 0 了呢 如果是大于 0 的话,那么就让它等于什么呢,等于 t 减 6 否则的话怎么样呢,我们让这个值怎么样呢等于 1,所以正确的写法应该是 t 减 6 是不是大于 0 的,我们要判断一下,break 然后再来是 case 7 8,case 9,在这样三种情况下怎么样呢,是向右一格 t 加 等于 我们让它向右移动一格 break 那么当我们把这样的一个 switch 怎么样呢,当我们 把这样的 一个 switch 写完了之后,我们就计算出来了一个新的这样的 一个 t 的位置,新的 t 的位置,随后怎么样呢,随后我们 [空白_录音] return t。 那么这样的返回这样的一个新的 t 的位置我们就完成了这样的一个 get position 这样的一个函数的这样的一个编写 基本上是这样的一个情况 空行删掉,我们屏幕有限,为了节约屏幕起见我们把多余的空行删掉了 那么这是我们创建了这样的一个乌龟类,这样的一个 乌龟类。 同样的,那么既然有这样的一个乌龟类,那么肯定应该有这样的一个 这样的一个兔子类 那么在这样的一个兔子类里面,它其实它的大部分代码怎么样,跟乌龟类的代码怎么样呢,是- 相同的,对吧,是相同的 所以我们懒惰起见我们可以直接怎么样呢,把这样的一个 把这样的一个乌龟类的代码怎么样,直接 copy 一下,copy 一下 然后我们仍然使用了这样的一个 当然了我们还要把这个东西改一改,改一改,对吧 呃…我们为了增加清楚起见,拿、 使用变量 t 也是可以 的,我们现在使用 h 怎么样,来表示兔子的这样的一个位置 然后对构造函数的名字要怎么样呢,要改写成 hare,这样的一个 然后我们也产生了这样的一个随机数,然后我们对 h 的位置怎么样呢,进行更新 而兔子的这样的一个规则当然跟乌龟是不一样的,我们看一下兔子的规则 兔子的规则是百分之二十的情况下怎么样呢,不动 那么百分之二十的情况下是向右九格 改一下,百分之二十的情况下 怎么样呢,不动,直接 break,不改变任何这样的一个 h 的值 百分之二十的情况下怎么样呢,是向右九格,h 加 等于 9,向右这样的一个九格 然后我们再来看一下,百分之十的情况下是向左十二格 百分之二十,百分之二十,在百分之十的情况下应该是向左十二格,h 是应该等于 h 减去 12 当然了,我们也同样要判断一下怎么样呢,它是 不是已经跌出了这样的起跑线之外了,如果是的话 我们要把它拉回来 break 然后再我们往下看 百分之三十的情况下怎么样呢,向右一格,以及百分之二十的情况下,向 左两格,再下来百分之三十的 情况下,百分之三十的情况下,应该是向 右一格,case 5 6 7 case 5 6 7,应该是 h h ,向右一格 break,然后再下来,百分之二十的情况下是 向…我看一下 向左两格,向左两格,那应该是 h 等于 h 减 2 再判断下它是不是大于 0 的 [空白_录音] 那接后我们 return 这个h 的值,那么就是这样的一个乌龟的这样的一个 呃…底下有一道红线,忘了写 break 忘了写 break 好,那么我们这样有这样的一个 兔子的这样的一个更新的这样的一个方式。 那么到此为止我们已经创建了 两个基本上是差不多的类,差不多的类对吧。 一个描述了这样的一个乌龟的这样的 一个跑步的这样的规则,一个描述了兔子这样的规则。 下来我们应该 再创建一个类,再创建一个类怎么样,作为整个这样的一个 跑步的这样的一个竞赛的这样的一个类,我们把它称为类 run running,跑步的这样的一个类 在这样的一个类里面,在这样的类里面我们声明两个对象 private,我们首先声明一个什么呢,首先声明一个 乌龟,乌龟类,那么假设这样的一个乌龟叫做一个 t 同时我们也创建一个 兔子的这样的一个就叫 h,那这里我们多说一句话 多强调一下,当我声明了这两个的时候,其实我只是声明了这两个 t 跟 h 两个要指向什么呢,要指向乌龟和兔子的一个对象的距离 或者引用,还没有实际的乌龟跟兔子的对象的创建 只有 new 的时候怎么样,用到 new 进行内存分配的时候才会真正地创建乌龟跟兔子的对象 我们可以把 new 直接写到后面,直接写到后面,所以讲我们可以直接写 t 等于 new 那么这样的一个新的这样的一个乌龟 以及 h 等于 h 等于 new 新的这样的一个兔子 当然了我们也可以把这两段话怎么样呢,写到这个 run 的这样的一个构造函数里面去 这两种写法在 C# 里面都是可以的 当然对于别的语言,比如讲C++ ,那你只能把它写到构造函数里面去,不能这样写的 那么既然我们已经把兔子跟乌龟都已经分配完毕了 那么貌似我们好像不需要这样的一个构造函数了? 好了,那么下来怎么样呢,下来我们提供这样的一个 public,public 这样的一个 这样的一个 run 的函数 来真正的把这样的让乌龟跟兔子怎么样呢,跑起来 让乌龟跟兔子跑起来。 那么 run run 这个函数怎么样呢,首先 我们得到这样的一个乌龟跟兔子的这样的一个位置 int t position 等于 就是乌龟 t 点 get position,得到一个新的乌龟的位置 int,int 这样的一个 h position 等于 h 点 get 得到一个新的兔子的位置。 当然了,在跑之前我们按照这个 原来这个例20描述的,我们可能还要出一些 煽动性的话语, run write line 煽动性的话语是怎么样说的呢 我们来看一下片子,起跑时显示 on your mark,get set on your mark 起跑线上准备,get set, 就是我们说的各就各位 然后写的是 Con. WriteLine 下面我们说预备,跑! 我们发了一枪,跑了。 and they are off,它们出去了 这个也我们有一个发令枪的声音,iii 然后 Console. WriteLine 的,我们有一个 [无声] 我们飞奔出去 好了,我们写了这样的三句话,我们开始这样的一个位置 那么对于这样的一个得到了这样的一个新的位置 那么我们底下有一个循环,那么这个循环怎么样呢?每次都要 直到有人冲过了这样的一个 70 的位置 也就是说当什么到 while,当这个 t tpOs 要小于等于 70 并且 h 它也小于等于70 这就是什么呢?没有任何一个选手现在冲过了这样的一个 终点线的时候,怎么样呢?我们就要不断地进行这样的一个兔子的奔跑 我们首先在怎么样呢?要首先显示一下这样的一个 当前的兔子跟这样的一个乌龟的位置,那么我们将会再写一个 方法来显示叫 display 所以说我们可以调用这样的一个 dis 一个我们先要构造的目前我们还没有写的一个方法,pry, display 它这个 display 里面我们传递一个乌龟的 位置 tpOs 以及一个兔子的位置 htpOs 来显示这样的一个当前乌龟跟兔子所在的这样的一个位置 那么显示完了之后干什么事情呢?显示完了之后 我们将再次调用这个 GetPosition, t.Position 怎么样呢?来得到新的 这样的一个兔子跟乌龟的位置,并且判断它们是不是有人 超出了这样的一个 70 ,当然在新的一轮循环的时候 我们要休眠一秒时的,我们不要,这个画面不要闪动的太快 那么休眠这个函数我们可以直接调用 .net system 里的这样的一个命名空间里面的这个 现成的 threading 的空间 system 里面的这个 threading 名字空间里面的 thread 类里面有一个这样的一个 sleep 这样的一个静态的函数 我们给它一个,它后面要接收一个毫秒值 我们给它一个这样的休眠,这样的一个毫秒,一个毫秒 休眠完选好之后,我们将继续 那么如果从这个循环退出来的时候,就说明已经某个人的这样的一个位置已经超过 70 了,对吧 超过了 70,那么在这样超过 70 的时候,我们 根据我们刚才的规则,如果两个都超过了 70 的话,那么我们是算乌龟赢的 所以我们先看一下,来判断下乌龟,看看乌龟是不是超过了 70,乌龟的位置是什么呢?是 t tpOs,如果 tpOs 怎么样呢,大于了 70 的话 如果它大于 了 70 的话,那么我们简单地就显示点 显示什么呢?显示这样的一个乌龟赢 [无声] 当然了,如果乌龟没有超过 70 的话,那么肯定是 兔子赢。 你从这个循环退出来之后,肯定是有一个选手已经冲出了 冲过了这样的一个终点线 冲过了终点线。 那么这样的 我们这个 run 这样的一个函数到此就这样的结束了,就结束了 iiii我们还需要有一个支撑的函数,显示函数没有写 那么这个 display 这个函数它应该申明成一个 private 的 因为我们不需要从类的外面直接调用这个display 函数 它仅仅是供类的内部 run 这个函数来调用的 所以我们把 display 怎么样呢,生成为一个 while 的 private void 的 display int, 一个 乌龟的位置,int 一个兔子的位置 因为它传递了这样的两个函数,这样的两个函数 那么这时我们会看到我们是来支持这样的一个 display 这样的一个 它首先传递乌龟和兔子的位置,兔子的位置,然后我们来想想该怎么样呢 显示乌龟跟兔子的位置呢? 那么我们为了这样的一个,当然了你可以逐步地 逐步地来通过一个循环不断来使用空格在屏幕上打印一些乌龟跟兔子的这样的一些位置 那么完了我们也可以这样做,我们也可以申明一个具有这样的一个具有 70 个元素的这样的一个速度 然后我们把这个速度的位置都填充好了之后,再把速度 转换成字符串,依次在这样的屏幕上打印出来 那么也是可以的。 我们用一下我们的 char 我们申明一个这样的一个字符型的速度,那么就叫做 ids,我们要显示的这样一个速度 那么它一共,当然了一共有 70 个格子,所以说它一共里面有 70 个这样的一个元素,其中iiii对应着我们的这样的一个 位置一,这个写法写的不对 写成 c++ 的写法了。 那么应该是这样写的,dis 应该等于 new char [无声] 那么有了这样的一个位置了之后,我们现在来看 我们现在来看我们首先怎么样呢?我们首先应该 应该把这样的一个速度怎么样呢,全部填充上什么呢 空格,全部填充成空格。 所以我们做成一个循环 for [无声] [无声] 在这个循环里面 单引号空格单引号 那么把它全部怎么样呢填充为空格。 然后我们在需要的位置上再来显示我们要的东西 那么首先我们第一个需要判断的就是 就是乌龟跟兔子发生了这样的位置重合的时候,也就是t 这样的一个 tpos 的位置应该和这样一个的 h 的这个位置怎么样呢 重合的时候,那么重合的时候按着这样的一个例子的这样的一个要求,它应该在重合的位置上 显示这样的一个iii,这样的一个发生咬的一个字符 那么如果在 t 的位置上重合了,那么由于我们速度的 0 对应的位置 是 1,那么在 t 的位置上重合了,那么对应到速度里面是应该在一个 t t 减1 的位置上开始显示,t 减1 的位置上开始显示。 当然了这个 我们这个重合的位置,t 的位置肯定是应该是 t position, s position肯定是应该是大于 1 的 是我们前面已经保证过的。 所以说应该在一开始在重合的位置上 进行显示一样的位置,那么我们简单地做一些赋值吧 应该在 t position 的位置上开始,position 减 1 的位置上开始应该怎么样呢 应该是有一个字符 o 写错位置了,应该是 dis 在这样的一个位置上有一个 o,对吧,在这样的一个位置上有一个 o。 我们把iii先拷贝起来 然后分别在这个 下一个位置上应该显示,注意我们应该字符的操作应该是 一个一个的来,或者说你用一个其他的什么拷贝函数进行这样的一个拷贝 而不应该能够直接的复制 o, u, c 然后我们加2,o, u, c, h 那么按照这个要求 还要有这样的一个三个感叹号,那么我们就写一个感叹号就行了 不去写那么多,ouch 这样呢发生了这样的一个 drill,发生了这样的一个drill,那么 否则的话,iii 如果 如果没有 t 的位置不重合 的话,那么就应该在 t 的这样一个位置上怎么样呢,显示一个大写的 T,在 h 的位置上显示一个大写的这样的一个 H。 所以说,如果没有发生这个追扰的话,那么 应该在第三了,对应的数组的位置应该是第 tPost 应该怎么样呢,.1 的位置上 应该放上一个大写的 T, 而应在这个 在这个 h 的这个 iii 的位置上应该怎么样呢,放上一个大写的 H, 大写的 H,这个标识当前兔子跟乌龟的位置。 那么把这样的一个位置的事情搞定了之后,那么我们 在最后的第 70 个线子上,也就是说数组的第 69 个的位置上, 那么我们应该显示一个什么呢,显示一个终点线, 应该等于单引号,还有竖杠,我们用一个竖杠怎么样,来表示这样的一个 终点线。 那么把这样的一个数组按照这样的意愿填充好了之后, 我们就直接怎么样呢,显示这样的一个数组里面的内容,Console. Writeline,那么在显示数组之前我们 new 把这个数组转化为 string,否则的话我们还得用一个循环,new string, 括号, 那么顺便说一下这样的一个写法叫做 一个命名的对象,我们声明了一个 new string ,但是却并没有拿一个什么 string a=new string 来接收它,我们直接传递给了 writeline, 通过这样的一个字符数组构造了一个新的字符串,并且直接来显示它 命名的这样声明的这样的一个命名的对象,声明的命名的对象来显示它。 所以说我们整个这样的一个龟兔赛跑的这样的一个 显示的这样一个函数,那么做完了这样的函数之后我们应该怎么样呢? 可以来测试一下这样的龟兔赛跑,如果有问题的话我们还要进入到这样一个 调试的环节,来修改自己的程序,看它对不对,这是一个编程的这样一个完整的过程。 那么为了测试这样的一个程序,我们首先创建一个 running 的对象,那么它是 r 1,创建了一个兔子跟一个乌龟的 new 的一个 running,那么有了这样的一个 running 的对象了之后我们调用 这样的一个 r 1. 这样的一个 run 这样的函数,怎么样呢, 方法来进行这样的一个龟兔的赛跑,龟兔的赛跑。 然后按下这个 Ctrl f5,我们先看一下这样的一个运行的结果。 这边拉宽一点,使得这个线在第 70 个位置上。 我们看到这是这样的一个兔子跟乌龟在这样的一个 在每隔一秒钟打印了它们之间的位置在进行的跑步。 看出来我们的程序的运行还是算是不错的。 兔子目前远远 领先于乌龟,现在兔子又往回跌了, 我们这样的一个跑步需要一段时间 才能够激烈的竞争,才能够分出这样的一个胜负。 乌龟现在超过了兔子,还追的很紧。 乌龟现在和兔子 诶,我们看到这个表了,显示 了 OUC,但是这个 H 没有出来,这个感叹号,那我们那块有个小小的问题。 那么等一会儿回头去看一下。 它们在不断的这样的一个奔跑,兔子向前冲锋。 兔子已经快接近了终点线,最后兔子赢了。 那么我们这个程序也就结束了。 那么我们会看到当时追赶的时候可能有一些小小的问题,我们把这个小小的问题 稍微改一下,我们 ouch 这块应该加 3, 加 3,然后显示感叹号,就没有什么太大的问题了,那么这样 那么到了这时候,我们顺便的想一下,这个面向对象的编程 是不是有什么,带来什么益处呢?譬如讲如果我用两个兔子跟两个乌龟在跑, 那么是不是我可以再创建一个对象, 就能够让两个乌龟跟两个兔子一块跑了呢? 那么这时候 run,如果我们再创建一个 r2= new running 的话,那么我们看到,其实这样的事情是 不可能发生的,因为你必须在 r 1 这个 run 结束了 之后才会有这样的一个 r2 这样的一个语句的执行, 如果你要想两个兔子跟两个乌龟交替跑的话, 那么需要怎么样呢,需要我们可能要做这样的一个 把它放到让它能够同时运行,但是我们 如果有兴趣的这样的一个学员的话,那么改正一下程序,把它放到一个线层上面去运行,看它- 是不是能够达到 两个兔子跟两个乌龟同时赛跑的情况,那么大家去思考吧。 好了,关于这个例子我们就讲述 到这。