2. 自动售货机
任务目标
设计一个自动售货机FSM,接受硬币(1元、5元)和纸币(10元、20元),商品单价为3元。系统根据累计投入金额决定是否出货,并在出货后计算找零。投入金额不足时,等待继续投币或取消退款。
状态定义与转移逻辑
FSM包含4个状态:IDLE(空闲等待)、ACCEPTING(接收投币)、DISPENSE(出货找零)、REFUND(退款)。核心逻辑:
- IDLE状态下投入任意金额 → 进入ACCEPTING,累计金额
- ACCEPTING状态下:继续投币则累加金额;累计金额≥3元时触发出货;按下取消键则进入REFUND
- DISPENSE状态:出货并计算找零(投入金额−3元),完成后回到IDLE
- REFUND状态:退还全部累计金额,完成后回到IDLE
完整实现(Python)
from enum import Enum
class State(Enum):
IDLE = 0
ACCEPTING = 1
DISPENSE = 2
REFUND = 3
class VendingMachine:
def __init__(self, price=3):
self.price = price
self.state = State.IDLE
self.balance = 0
def insert(self, amount):
"""接受硬币/纸币,返回操作提示"""
if amount not in (1, 5, 10, 20):
return f"无效面额: {amount}元"
if self.state == State.IDLE:
self.state = State.ACCEPTING
self.balance = amount
return self._status()
elif self.state == State.ACCEPTING:
self.balance += amount
if self.balance >= self.price:
return self._dispense()
return self._status()
else:
return f"当前状态({self.state.name})不可投币"
def cancel(self):
"""取消交易,退还所有金额"""
if self.state == State.ACCEPTING:
refund = self.balance
self.balance = 0
self.state = State.IDLE
return f"已退款 {refund}元"
return "当前状态不可取消"
def _dispense(self):
"""出货并找零"""
change = self.balance - self.price
self.balance = 0
self.state = State.IDLE
msg = f"出货成功,找零 {change}元"
return msg
def _status(self):
return f"已投入 {self.balance}元,还需 {self.price - self.balance}元"
# 最小验证闭环
if __name__ == "__main__":
vm = VendingMachine()
print(vm.insert(1)) # 已投入 1元,还需 2元
print(vm.insert(5)) # 出货成功,找零 3元
print(vm.insert(10)) # 已投入 10元,还需 0元(状态重置后重新投币)
print(vm.cancel()) # 已退款 10元
运行与验证
直接运行脚本:python vending_machine.py。预期输出顺序为:
已投入 1元,还需 2元
出货成功,找零 3元
已投入 10元,还需 0元
已退款 10元
验证通过条件:前两行验证投币1元后补投5元,累计6元,出货并找零3元;后两行验证出货后状态重置,新交易投入10元后取消,全额退款。若输出与上述一致,则FSM状态转移正确。
状态转移图
关键说明
- 投币面额限定为1、5、10、20元,其他面额直接拒绝;如需扩展面额,修改
insert()中的校验元组即可。 - 出货条件为累计金额≥3元,找零金额自动计算;若投入恰好3元,找零为0元。
- 取消操作仅在ACCEPTING状态有效,退款后状态回到IDLE,余额清零。