鱼C论坛

 找回密码
 立即注册
查看: 1355|回复: 3

关于旧版python教程第037讲,写一个乌龟和鱼的小游戏

[复制链接]
发表于 2020-1-30 20:17:27 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 囧囧JOJO 于 2020-1-30 22:37 编辑

萌新嘛也不懂,感觉自己写的对了,但是运行结果看,几乎没有可能鱼全被吃掉。
先说说我的代码吧。
阅读我代码之前,我先解释一下几个难理解的变量命名具体代表什么。。
range_x表示场景范围的x坐标
range_y表示场景范围的y坐标(也即从(0,0)到(10,10)的121个位置。)
鱼类里面的Fx属性为fish x,就是鱼的x坐标,同理Fy表示鱼的y坐标
way表示4个数里面随机生成的一个数,用来说明朝哪个方向走(上下左右),后面也有说明。
乌龟类的HP属性表示乌龟的血量
同理乌龟类里面的属性TX,Ty分别表示Turtle x,Turtle y乌龟的x,y坐标。
fish=[]的这个列表用来储存鱼,初始列表10个值,也即10条鱼
step顾名思义,就是乌龟需要一次走几步(1或2步),但是为了循环方便,它的值为(0和1)




先把我写的代码整体复制过来:
import random as r

#先生成场景范围(x,y)
range_x = 10
range_y = 10



class Fish:
    def __init__(self):
        self.Fx = r.randint(0,range_x)
        self.Fy = r.randint(0,range_y)
    def move(self):
        way = r.randint(-2,2)#确定鱼怎么走(1上,-1下,-2左,2右)
        while way == 0 :#生成的way只能是-2,-1,1,2,不能为0
            way = r.randint(-2,2)
        if(abs(way) == 1):#判断鱼为上下方向移动
            if(way > 0):
                self.Fy += 1
            else:
                self.Fy -= 1
            if(self.Fy < 0):
                self.Fy += 2
            elif(self.Fy > range_y):
                self.Fy -= 2

        if(abs(way) == 2):#判断鱼为左右方向移动
            if(way > 0):
                self.Fx += 1
            else:
                self.Fx -= 1
            if(self.Fx < 0):
                self.Fx += 2
            elif(self.Fx > range_x):
                self.Fx -= 2


class Turtle:
    def __init__(self):
        self.HP = 100
        self.Tx ,self.Ty= r.randint(0,range_x),r.randint(0,range_y)#生成初始乌龟的位置
    def move(self):
        way = r.randint(-2,2)#确定乌龟怎么走(1上,-1下,-2左,2右)
        while way == 0 :#生成的way只能是-2,-1,1,2,不能为0
            way = r.randint(-2,2)

        if(abs(way) == 1):#判断乌龟为上下方向移动
            if(way > 0):
                self.Ty += 1
            else:
                self.Ty -= 1
            if(self.Ty < 0):
                self.Ty += 2
            elif(self.Ty > range_y):
                self.Ty -= 2

        if(abs(way) == 2):#判断乌龟为左右方向移动
            if(way > 0):
                self.Tx += 1
            else:
                self.Tx -= 1
            if(self.Tx < 0):
                self.Tx += 2
            elif(self.Tx > range_x):
                self.Tx -= 2
                    
'''
                ↑(-1)
                
     ←(-2)   0    →(2)

                 ↓(1)
            '''
t = Turtle()#初始化(血量,位置)乌龟
fish = []
for i in range(10):
    fish.append(Fish())#创建10个鱼列表,


while True:
    step = r.randint(0,1)
    #乌龟一次走几步(1-2)
    t.HP -= (step+1) #乌龟血量减去移动次数
    for i in range(step):
        t.move()
    for i in range(len(fish))[::-1]:
        fish[i].move()
        if (fish[i].Fx == t.Tx and fish[i].Fy == t.Ty):
            del fish[i]
            print('有一条鱼儿被吃掉了...')
            if (t.HP+20>100):
                t.HP = 100
            else:
                t.HP += 20
    if len(fish) == 0:
        print('鱼儿都吃完了,游戏结束!')
        break
    if(t.HP <= 0):
        print('乌龟体力耗尽,挂掉了!')
        break

根据题意:假设游戏场景为范围(x, y)为0<=x<=10,0<=y<=10,我设置了地图的大小(x,y),即从(0,0)到(10,10),共121个位置。
import random as r

#先生成场景范围(x,y)
range_x = 10
range_y = 10

接着就是两个类(乌龟类 和 鱼类)
按照代码运行顺序看吧。首先是t = Turtle(),注释也写了,初始化乌龟的血量和初始位置,在乌龟类里面是这样的:
class Turtle:
    def __init__(self):
        self.HP = 100
        self.Tx ,self.Ty= r.randint(0,range_x),r.randint(0,range_y)#生成初始乌龟的位置
接下来,代码就创建了10条鱼,并且每条鱼都加入列表fish里面了,而创建鱼的代码在 鱼类 里面,如下:
class Fish:
    def __init__(self):
        self.Fx = r.randint(0,range_x)
        self.Fy = r.randint(0,range_y)
至此,准备工作已经完成了,现在就是正式move过程,我把move过程写进一个死循环,直到鱼死或者乌龟死,才跳出循环,具体如下:
while True:
    step = r.randint(0,1)
    #乌龟一次走几步(1-2)
    t.HP -= (step+1) #乌龟血量减去移动次数
    for i in range(step):
        t.move()
    for i in range(len(fish))[::-1]:
        fish[i].move()
        if (fish[i].Fx == t.Tx and fish[i].Fy == t.Ty):
            del fish[i]
            print('有一条鱼儿被吃掉了...')
            if (t.HP+20>100):
                t.HP = 100
            else:
                t.HP += 20
    if len(fish) == 0:
        print('鱼儿都吃完了,游戏结束!')
        break
    if(t.HP <= 0):
        print('乌龟体力耗尽,挂掉了!')
        break
先确定乌龟一次走几步,然后把走路消耗的血扣掉,接着一个循环(一次移动两步或者一步),让乌龟完成一次的移动。
再接着,十只鱼(刚开始10只,后来死了就不是十只)要逐个移动,写进for循环里,因为循环里要把鱼跟乌龟同一个位置的鱼杀死,所以我倒序移动鱼(即第十条先移动,接着第九条... ...),for循环里的代码也简单,就是鱼移动,然后判断是不是跟乌龟同一个位置,是就删除fish列表中这条鱼,接着给乌龟加血20。
循环结束之后要确定与是不是都死了,或者乌龟是不是累死了,也就是两个if判断,很简单,我觉得我不需要解释了。。
接着就是两个类里面的方法,其实方法都是一样的,照搬照抄的,我只讲一个鱼的就行了。。
def move(self):
    way = r.randint(-2,2)#确定乌龟怎么走(1上,-1下,-2左,2右)
    while way == 0 :#生成的way只能是-2,-1,1,2,不能为0
        way = r.randint(-2,2)
    if(abs(way) == 1):#判断鱼为上下方向移动
        if(way > 0):
            self.Fy += 1
        else:
            self.Fy -= 1
        if(self.Fy < 0):
            self.Fy += 2
        elif(self.Fy > range_y):
            self.Fy -= 2

    if(abs(way) == 2):#判断鱼为左右方向移动
        if(way > 0):
            self.Fx += 1
        else:
            self.Fx -= 1
        if(self.Fx < 0):
            self.Fx += 2
        elif(self.Fx > range_x):
            self.Fx -= 2
判断上下左右哪个方向移动也就是生成(-2,-1,1,2)四个数字,-2表示向左,2表示向右,-1表示向上,1表示向下。
然后判断生成的这个数的绝对值是1还是2(abs(way)),如果是1表示上下方向移动,只用更改y坐标就行了,反之改x坐标。
我改坐标的方法是,先改,然后判断是否出界了,如果出界了就反方向再走两步就回来了(也就是题中说的当移动到场景边缘,自动向反方向移动),至此我代码也就写完了。问题就是几乎不可能让乌龟把鱼吃完。
我也尝试用小甲鱼答案里的方法运行,乌龟把鱼吃完的概率远远大于我写的。不清楚思路上有什么问题,下面附上小甲鱼写的参考答案(我也不能完全看懂,就直接贴出来吧。)
import random as r
 
legal_x = [0, 10]
legal_y = [0, 10]
 
class Turtle:
    def __init__(self):
        # 初始体力
        self.power = 100
        # 初始位置随机
        self.x = r.randint(legal_x[0], legal_x[1])
        self.y = r.randint(legal_y[0], legal_y[1])
 
    def move(self):
        # 随机计算方向并移动到新的位置(x, y)
        new_x = self.x + r.choice([1, 2, -1, -2])
        new_y = self.y + r.choice([1, 2, -1, -2])
        # 检查移动后是否超出场景x轴边界
        if new_x < legal_x[0]:
            self.x = legal_x[0] - (new_x - legal_x[0])
        elif new_x > legal_x[1]:
            self.x = legal_x[1] - (new_x - legal_x[1])
        else:
            self.x = new_x
        # 检查移动后是否超出场景y轴边界
        if new_y < legal_y[0]:
            self.y = legal_y[0] - (new_y - legal_y[0])
        elif new_y > legal_y[1]:
            self.y = legal_y[1] - (new_y - legal_y[1])
        else:
            self.y = new_y        
        # 体力消耗
        self.power -= 1
        # 返回移动后的新位置
        return (self.x, self.y)
 
    def eat(self):
        self.power += 20
        if self.power > 100:
            self.power = 100
 
class Fish:
    def __init__(self):
        self.x = r.randint(legal_x[0], legal_x[1])
        self.y = r.randint(legal_y[0], legal_y[1])
        
    def move(self):
        # 随机计算方向并移动到新的位置(x, y)
        new_x = self.x + r.choice([1, -1])
        new_y = self.y + r.choice([1, -1])
        # 检查移动后是否超出场景x轴边界
        if new_x < legal_x[0]:
            self.x = legal_x[0] - (new_x - legal_x[0])
        elif new_x > legal_x[1]:
            self.x = legal_x[1] - (new_x - legal_x[1])
        else:
            self.x = new_x
        # 检查移动后是否超出场景y轴边界
        if new_y < legal_y[0]:
            self.y = legal_y[0] - (new_y - legal_y[0])
        elif new_y > legal_y[1]:
            self.y = legal_y[1] - (new_y - legal_y[1])
        else:
            self.y = new_y
        # 返回移动后的新位置
        return (self.x, self.y)
 
turtle = Turtle()
fish = []
for i in range(10):
    new_fish = Fish()
    fish.append(new_fish)
 
while True:
    if not len(fish):
        print("鱼儿都吃完了,游戏结束!")
        break
    if not turtle.power:
        print("乌龟体力耗尽,挂掉了!")
        break
 
    pos = turtle.move()
    # 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用
    # 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^
    for each_fish in fish[:]:
        if each_fish.move() == pos:
            # 鱼儿被吃掉了
            turtle.eat()
            fish.remove(each_fish)
            print("有一条鱼儿被吃掉了...")

小弟不才,只能这样解释了,望大佬帮忙指导,俺也想追随大佬的脚步啊!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-30 22:36:36 | 显示全部楼层
自己解决了。
不知道是我理解题意的问题还是怎么回事,我现在还是觉得我写的是对的。
原题中:乌龟的最大移动能力是2(Ta可以随机选择1还是2移动),鱼儿的最大移动能力是1,意思也就是说,乌龟一次最多走2步,最少走1步,然而,自习看了小甲鱼的代码发现,乌龟单次最多可以走4步,最少可以走2步(x,y方向分别走1~2步)。
现在还是觉得我的是对的。。
明天改写一下这个程序,算出来并比较两种算法鱼全部被吃的概率,计算方式只差一点,可结果却感觉差了很远。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-31 12:12:29 | 显示全部楼层
一万次的计算结果:

我的第一版的源码:
10000次试验中,鱼儿被吃完了33次,乌龟累死了9967次,鱼儿被吃完的概率为0.33%,乌龟累死的概率为99.67%。

我的第二版的源码:
10000次试验中,鱼儿被吃完了1306次,乌龟累死了8694次,鱼儿被吃完的概率为13.06%,乌龟累死的概率为86.94%。

小甲鱼的源码:
10000次试验中,鱼儿被吃完了1665次,乌龟累死了8694次,鱼儿被吃完的概率为16.65%,乌龟累死的概率为86.94%。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2020-7-13 18:29:13 | 显示全部楼层
666
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-20 01:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表