暗语寒飞 发表于 2020-10-20 10:38

[新人娱乐向]电梯模拟器

学习 python 后一直没有什么想要写的东西, 工作上开发使用的是 php 语言, 一时间也改变不了.

然后在某个网站(网址忘记了)看到了一个 web 版的电梯模拟器, 玩了半天, 他的电梯运行逻辑各种情况下都有问题, 这就萌生了我自己写一个 python 版的想法.

当时不会 GUI, 只能把展示内容写在文件里, 这样, 我打开展示文件(只读), 然后执行程序, 文件中的内容会一秒刷新一次 (偷懒版动画, 哈哈)

现在学了 GUI, 发呆发了大半天也没写出来, 就先放弃了


先展示一下效果:



图中红色的是 vscode 的一个插件, 检测无效空格的. 名字叫 `Trailing Spaces`

#### 项目地址 (https://github.com/anyuhanfei/Elevator_simulation)

用 vscode 打开展示文件, 然后不要进行任何操作, 否则 vscode 会获取写权限, 这样程序就没办法更新文件了.

#### 还有什么好玩的能写出来的吗?


接下来是代码, 大概思路就是, 把大楼, 电梯, 乘客分别封装成类, 包含属性和所有可能的动作. 执行文件中循环执行电梯运行逻辑, 并且电梯运行一次, 展示文件更新一次.

### 大楼
```
class mansion():
    maximum_layer = None# 最高层数
    minimum_layer = 1# 最低层数
    data = {}# 每层电梯情况

    def __init__(self, maximum_layer, minimum_layer=None):
      self.maximum_layer = maximum_layer
      self.minimum_layer = minimum_layer if minimum_layer is not None else self.minimum_layer
      self._init_data()

    def _init_data(self):
      '''初始化电梯具体数据'''
      for i in range(self.minimum_layer, self.maximum_layer + 1):
            if i != 0:
                self.data.update({'%s' % (i): {'up': False, 'down': False}})

    def add_task(self, layer, orientation):
      '''乘客按电梯
      将指定楼层的上状态或下状态设置开
      '''
      if orientation == 'up' or orientation == 'down':
            self.data = True
            return True
      else:
            return False

    def over_task(self, layer, orientation):
      '''接到乘客
      将指定楼层的上状态或下状态设置关
      '''
      if orientation == 'up' or orientation == 'down':
            self.data = False
            return True
      else:
            return False

```

### 电梯
```
class elevator():
    maximum_load = 460# kg最大载重
    current_layer = 1# 当前所在层数,初始化为1(电梯无0层)
    passenger_number = 0# 乘客数量,初始化为0
    passenger_total_weight = 0# 乘客总重量,初始化为0
    move_task = {'up': set(), 'down': set()}# 移动任务,乘客需要到达的层数
    move_orientation = 'up'# 移动方向

    def __init__(self, maximum_load=None):
      self.maximum_load = self.maximum_load if maximum_load is None else maximum_load

    def check_mansion_data(self, mansion_obj):
      '''检测大楼状态
      若有新增的移动任务,则添加
      '''
      for key, value in mansion_obj.data.items():
            if value['up'] is True:
                self.move_task['up'].add(int(key))
            if value['down'] is True:
                self.move_task['down'].add(int(key))

    def move_self(self, minimum_layer, maximum_layer):
      '''移动自己
      每一时间单位执行一次,移动一层
      '''
      if self.update_orientation() is False:
            return
      # 到达顶层没有停留,改变方向后向下了一格(找不到bug,无奈之举)
      if maximum_layer == self.current_layer and self.move_orientation == 'up':
            return
      if minimum_layer == self.current_layer and self.move_orientation == 'down':
            return

      self.current_layer = self.current_layer + 1 if self.move_orientation == 'up' else self.current_layer - 1
      if self.current_layer == 0:
            self.current_layer = self.current_layer + 1 if self.move_orientation == 'up' else self.current_layer - 1

    def add_move_task(self, passenger_obj):
      '''乘客进入电梯完成后,添加移动任务'''
      self.move_task.add(passenger_obj.want_layer)

    def check_overload(self, passenger_obj):
      '''超载检测'''
      if self.passenger_total_weight + passenger_obj.weight > self.maximum_load:
            return False
      else:
            self.passenger_number += 1
            self.passenger_total_weight += passenger_obj.weight
            return True

    def check_open_door(self, minimum_layer, maximum_layer):
      '''检测是否开门(即停止)'''
      try:
            self.move_task.remove(self.current_layer)
            # self.update_orientation()
            return True
      except BaseException:
            # 不应该开门
            return False

    def remove_passenger(self, passenger_obj):
      '''乘客到达'''
      self.passenger_number -= 1
      self.passenger_total_weight -= passenger_obj.weight

    def update_orientation(self):
      '''更新方向'''
      temp = False
      if self.move_orientation == 'up':# 检测向上任务中有没有大于我所在层数的
            for i in (self.move_task['down'] | self.move_task['up']):
                if int(i) > self.current_layer:
                  temp = True
                  break
      elif self.move_orientation == 'down':# 检测向下任务中有没有小于我所在层数的
            for i in (self.move_task['down'] | self.move_task['up']):
                if int(i) < self.current_layer:
                  temp = True
                  break
      if temp is False:
            self.move_orientation = 'down' if self.move_orientation == 'up' else 'up'
      return temp

```

### 乘客
```
import random

class passenger():
    weight = None# 体重,40~100之间
    current_layer = None# 当前所在层数
    want_layer = None# 想要到达层数
    orientation = None# 方向
    status = False# 当前乘坐状态,False为等待中,True为乘坐中

    def __init__(self, max_layer, min_layer, weight=None, current_layer=None, want_layer=None):
      self.weight = self._random_weight() if weight is None else weight
      self.current_layer = self._random_current_layer(min_layer, max_layer) if current_layer is None else current_layer
      self.want_layer = self._random_want_layer(min_layer, max_layer) if want_layer is None else want_layer
      self.orientation = 'up' if self.current_layer < self.want_layer else 'down'

    def start_task(self):
      '''开始任务
      在大楼上按电梯按钮,这里只需要返回大楼对象要操作的楼层和上下方向即可
      '''
      return self.current_layer, 'up' if self.current_layer < self.want_layer else 'down'

    def in_elevator(self):
      '''进入电梯'''
      self.status = True

    def elevator_overload(self):
      '''电梯超载
      重新开始任务
      '''
      self.status = False
      return self.start_task()

    def whether_to_over(self, elevator_obj):
      '''是否达到目的层数'''
      if elevator_obj.current_layer == self.want_layer:
            return True
      else:
            return False

    def _random_weight(self):
      '''随机生成体重
      40~50公斤几率:20%
      50~60公斤几率:35%
      60~70公斤几率:30%
      70~80公斤几率:10%
      80~100公斤几率:5%
      '''
      weight_level =
      weight_level_random =
      random.shuffle(weight_level_random)
      return random.randint(weight_level], weight_level + 1])

    def _random_current_layer(self, min_layer, max_layer):
      '''随机生成所在楼层
      1楼及其下楼层居多
      '''
      for i in range(min_layer, max_layer + 1):
            if i == 0:
                continue
            if i <= 1:
                if random.randint(0, 1) == 1:
                  return i
      return random.randint(2, max_layer)

    def _random_want_layer(self, min_layer, max_layer):
      '''随机生成要去楼层(不与所在楼层相同)
      根据所在楼层随机要求楼层,
      1层和负层:若有负层,高层居多,负层少
      高层:1层和负层居多,其他层少
      '''
      want_layer = 0
      if self.current_layer <= 1:
            want_layer = random.randint(min_layer, max_layer)
      else:
            if random.randint(0, 1) == 1:
                want_layer = random.randint(2, max_layer)
            else:
                want_layer = random.randint(min_layer, 1)
      if want_layer == 0 or want_layer == self.current_layer:
            want_layer = self._random_want_layer(min_layer, max_layer)
      return want_layer
```

### 参数
```
'''运行方式'''
RUN_MODE = 'auto'# manual or auto

'''运行间隔时间(自动模式生效)'''
UNIT_TIME = 2

'''楼层'''
MAXIMUM_LAYER = 7
MINIMUM_LAYER = 1

'''乘客生成概率'''
PGP = 2# 最低为1,数值越大,概率越低

'''电梯最大载重'''
MAXIMUM_LOAD = 200# None为默认载重
```

### 执行文件
```
import time
import random

import __init__
from element import mansion
from element import elevator
from element import passenger

# 大楼初始化
mansion_obj = mansion.mansion(__init__.MAXIMUM_LAYER, __init__.MINIMUM_LAYER)

# 电梯初始化
elevator_obj = elevator.elevator(__init__.MAXIMUM_LOAD)

# 乘客初始化
passenger_objs = dict()

# 开始运营
while(True):
    if __init__.RUN_MODE == 'manual':
      if input('请输入下一步指令:(N结束)') == 'N':
            break
    else:
      time.sleep(__init__.UNIT_TIME)

    elevator_obj.move_self(__init__.MINIMUM_LAYER, __init__.MAXIMUM_LAYER)
    # 随机生成乘客
    if random.randint(1, __init__.PGP) == 1:
      passenger_obj = passenger.passenger(__init__.MAXIMUM_LAYER, __init__.MINIMUM_LAYER)
      passenger_objs.update({str(time.time()): passenger_obj})
      # 按按钮
      layer, orientation = passenger_obj.start_task()
      mansion_obj.add_task(layer, orientation)
      # 电梯检索
      elevator_obj.check_mansion_data(mansion_obj)
    # 电梯移动,检测开门
    pop_passenger_keys = []
    if elevator_obj.check_open_door(__init__.MINIMUM_LAYER, __init__.MAXIMUM_LAYER) is True:
      for key, i in passenger_objs.items():
            if i.status is False:# 未上电梯
                if i.current_layer == elevator_obj.current_layer:# 电梯到达当前所在楼层
                  if i.orientation == elevator_obj.move_orientation:# 方向相同
                        i.in_elevator()# 乘客上电梯
                        if elevator_obj.check_overload(i) is True:# 超载检测
                            elevator_obj.add_move_task(i)# 添加移动任务
                            mansion_obj.over_task(i.current_layer, i.orientation)# 大楼电梯按钮关闭
                        else:
                            layer, orientation = i.elevator_overload()# 超载,重新按电梯
                            mansion_obj.add_task(layer, orientation)
            else:# 已上电梯
                if i.whether_to_over(elevator_obj) is True:# 到达目的地
                  # 乘客下电梯
                  elevator_obj.remove_passenger(i)
                  pop_passenger_keys.append(key)
    for key in pop_passenger_keys:
      passenger_objs.pop(key)
    elevator_obj.check_mansion_data(mansion_obj)# 电梯检索

    # 动态打印
    file_content = ''
    for i in range(__init__.MAXIMUM_LAYER, __init__.MINIMUM_LAYER - 1, -1):
      if i == 0:
            continue
      if i == elevator_obj.current_layer:
            file_content += '   电梯   '
      else:
            file_content += '          '
      file_content += '%s' % (i)
      file_content += '%s' % (' ↓ ' if mansion_obj.data['down'] is True else '   ')
      file_content += '%s' % (' ↑ ' if mansion_obj.data['up'] is True else '   ')
      file_content += '   '
      for key, value in passenger_objs.items():
            if value.status is False and value.current_layer == i:
                file_content += ' 人(%s) ' % (value.want_layer)
      file_content += '\n'
    # 电梯参数
    file_content += '\n'
    file_content += '电梯任务: %s\n电梯方向:%s\n' % (elevator_obj.move_task, elevator_obj.move_orientation)
    file_content += '所在层:%s\n当前载重:%s\n' % (elevator_obj.current_layer, elevator_obj.passenger_total_weight)
    file_content += '电梯乘客: '
    for key, value in passenger_objs.items():
      if value.status is True:
            file_content += ' 人(%s) ' % (value.want_layer)
    file_content += '\n'
    # 乘客参数
    file_content += '\n所有乘客:\n'
    for key, value in passenger_objs.items():
      file_content += '所在层:%s达到层:%s方向:%s' % (value.current_layer, value.want_layer, value.orientation)
      file_content += '乘坐状态:%s体重:%s\n' % (value.status, value.weight)
    # 保存到文件
    with open('show.log', 'w+', encoding="utf-8") as f:
      f.write(file_content)
```

sunbester 发表于 2020-10-20 10:42

不错,学习了

sludger 发表于 2020-10-20 11:06

赞一个,想学python,但是0基础不知道咋个开始

lucifer52 发表于 2020-10-20 20:45

挺不错的,对学习理解很有帮助。

暗语寒飞 发表于 2020-10-21 10:55

sludger 发表于 2020-10-20 11:06
赞一个,想学python,但是0基础不知道咋个开始

我是看***的 7月7大佬的python基础视频入的门, 你可以找找, 网络上有资源免费下载的, 吾爱应该就有   https://coding.imooc.com/class/chapter/136.html#Anchor

alexskyboy 发表于 2020-10-22 14:12

感谢分享,正好学习~~~~~~~~·

骑士计划 发表于 2020-10-25 17:52

感觉很有趣的样子,楼主辛苦啦:lol
页: [1]
查看完整版本: [新人娱乐向]电梯模拟器