技術準備

在開始編碼之前,我們需要準備開發環境和相關工具。以下是開發 物流大師 所需的技術棧和資源。

1. 技術棧

  • 編程語言:Python 3.x(推薦 3.8 或更高版本)。
  • 核心庫
  • random:生成隨機事件,如交通堵塞或客户訂單。
  • time:控制遊戲節奏和模擬時間流逝。
  • heapq:實現 Dijkstra 算法優化路線。
  • pygame(可選):用於圖形界面和運輸網絡可視化。
  • 數據存儲
  • 使用字典和列表管理城市、路線、車輛和訂單。
  • 圖結構表示城市網絡,邊權重為距離或時間。
  • 用户界面:基於命令行界面(CLI)顯示遊戲狀態和交互,圖形界面作為擴展。
  • 依賴安裝
  • randomtime 和 heapq 是 Python 標準庫,無需額外安裝。
  • pygame(可選):pip install pygame

2. 開發環境

確保已安裝 Python(可從 python.org 下載)。推薦使用 Visual Studio Code 或 PyCharm 作為開發工具,提供代碼補全和調試支持。本遊戲的核心版本基於 CLI,擴展部分引入圖形界面。項目結構如下:

logistics_game/
├── main.py           # 主程序入口
└── game.py           # 遊戲邏輯模塊

3. 遊戲目標

玩家將扮演物流公司經理,目標是在 30 個回合(模擬 30 天)內將公司聲譽提升到 100,同時保持資金正值。遊戲採用回合制,每回合代表一天,玩家需要:

  • 接受訂單:選擇客户訂單,包含貨物類型、重量和目的地。
  • 調度車輛:分配車輛執行訂單,優化路線。
  • 管理資源:平衡燃料成本、維護費用和員工工資。
  • 應對事件:處理隨機事件,保持運營穩定。

遊戲設計與核心功能

1. 遊戲結構

我們將遊戲分解為多個模塊,使用類和函數封裝功能,確保代碼清晰且易於維護。以下是核心設計:

  • City 類:表示城市,包含名稱和相鄰城市信息。
  • Vehicle 類:表示車輛,包含類型、容量、速度、燃料消耗等。
  • Order 類:表示客户訂單,包含貨物、重量、目的地和截止日期。
  • LogisticsGame 類:管理遊戲狀態、運輸網絡、車輛、訂單和主循環。
  • 算法
  • Dijkstra 算法:計算城市間最短路徑。
  • 貪心調度:根據車輛容量和訂單優先級分配任務。
  • 數據結構
  • 城市網絡:圖結構,節點為城市,邊為距離。
  • 車輛:包含位置、狀態(空閒/運輸中)和任務。
  • 訂單:包含貨物信息和收益。
  • 遊戲狀態:資金、聲譽、車輛列表、訂單列表。

2. 核心代碼實現

以下是遊戲的完整代碼框架,我們將逐一講解每個模塊的實現細節。

2.1 城市類:City

City 類定義城市及其連接。

# game.py
class City:
    def __init__(self, name):
        """初始化城市"""
        self.name = name
        self.neighbors = {}  # {鄰居城市: 距離}

    def add_neighbor(self, city, distance):
        """添加鄰居城市和距離"""
        self.neighbors[city] = distance

説明

  • __init__:初始化城市名稱和鄰居字典。
  • add_neighbor:添加相鄰城市和距離,構建圖結構。

2.2 車輛類:Vehicle

Vehicle 類定義車輛的屬性和行為。

# game.py (續)
class Vehicle:
    def __init__(self, name, vtype, capacity, speed, fuel_per_km):
        """初始化車輛"""
        self.name = name
        self.type = vtype  # "Van", "Truck"
        self.capacity = capacity  # 最大載重(噸)
        self.speed = speed  # 速度(km/h)
        self.fuel_per_km = fuel_per_km  # 每公里燃料成本
        self.location = None  # 當前所在城市
        self.current_order = None  # 當前運輸的訂單
        self.maintenance_cost = 100 if vtype == "Van" else 200  # 每日維護費用
        self.status = "Idle"  # Idle, Moving

    def assign_order(self, order, path):
        """分配訂單和路徑"""
        self.current_order = order
        self.status = "Moving"
        self.path = path  # 運輸路徑
        self.current_step = 0  # 當前路徑進度

    def move(self):
        """移動到路徑的下一個城市"""
        if self.status == "Moving" and self.current_step < len(self.path) - 1:
            self.current_step += 1
            self.location = self.path[self.current_step]
            return False
        elif self.status == "Moving":
            self.status = "Idle"
            self.current_order = None
            return True  # 訂單完成
        return False

説明

  • __init__:初始化車輛的名稱、類型、容量、速度和燃料成本。
  • assign_order:分配訂單和運輸路徑,切換狀態為“運輸中”。
  • move:沿路徑移動一步,到達終點則完成訂單。

2.3 訂單類:Order

Order 類定義客户訂單。

# game.py (續)
class Order:
    def __init__(self, id, weight, destination, revenue, deadline):
        """初始化訂單"""
        self.id = id
        self.weight = weight  # 貨物重量(噸)
        self.destination = destination  # 目的地城市
        self.revenue = revenue  # 收益
        self.deadline = deadline  # 截止天數

説明

  • __init__:初始化訂單的 ID、重量、目的地、收益和截止日期。

2.4 遊戲類:LogisticsGame

LogisticsGame 類管理遊戲狀態和邏輯。

# game.py (續)
import random
import time
import heapq

class LogisticsGame:
    def __init__(self):
        """初始化遊戲"""
        self.cities = self.create_cities()
        self.vehicles = []
        self.orders = []
        self.money = 10000  # 初始資金
        self.reputation = 50  # 初始聲譽
        self.turn = 0
        self.max_turns = 30
        self.game_over = False

    def create_cities(self):
        """創建城市網絡"""
        cities = {name: City(name) for name in ["A", "B", "C", "D", "E"]}
        # 添加連接(雙向邊)
        connections = [
            ("A", "B", 100), ("A", "C", 150), ("B", "C", 120),
            ("B", "D", 200), ("C", "D", 180), ("C", "E", 160),
            ("D", "E", 140)
        ]
        for city1, city2, distance in connections:
            cities[city1].add_neighbor(cities[city2], distance)
            cities[city2].add_neighbor(cities[city1], distance)
        return cities

    def dijkstra(self, start, end):
        """Dijkstra 算法計算最短路徑"""
        distances = {city: float("inf") for city in self.cities}
        distances[start] = 0
        pq = [(0, start)]
        predecessors = {city: None for city in self.cities}
        while pq:
            current_distance, current_city = heapq.heappop(pq)
            if current_city == end:
                break
            if current_distance > distances[current_city]:
                continue
            for neighbor, distance in current_city.neighbors.items():
                distance_to_neighbor = current_distance + distance
                if distance_to_neighbor < distances[neighbor]:
                    distances[neighbor] = distance_to_neighbor
                    predecessors[neighbor] = current_city
                    heapq.heappush(pq, (distance_to_neighbor, neighbor))
        path = []
        current = end
        while current:
            path.append(current)
            current = predecessors[current]
        return path[::-1], distances[end]

    def run(self):
        """遊戲主循環"""
        print("歡迎來到《物流大師》!目標:在 30 天內將聲譽提升到 100。")
        self.vehicles.append(Vehicle("Van1", "Van", 2, 60, 0.5))
        self.vehicles[0].location = self.cities["A"]
        while not self.game_over:
            self.display_state()
            self.generate_orders()
            action = self.get_action()
            if action == "buy_vehicle":
                self.buy_vehicle()
            elif action == "accept_order":
                self.accept_order()
            elif action == "end_turn":
                self.end_turn()
            if self.check_win():
                print("恭喜!你成功將聲譽提升到 100!")
                self.game_over = True
            if self.check_lose():
                print("遊戲結束。未能在 30 天內達成目標,或資金耗盡。")
                self.game_over = True
            time.sleep(1)

    def display_state(self):
        """顯示遊戲狀態"""
        print(f"\n天數 {self.turn + 1}")
        print(f"資金: {self.money}")
        print(f"聲譽: {self.reputation}")
        print("車輛:", [f"{v.name} ({v.type}, {v.status}, {v.location.name})" for v in self.vehicles])
        print("訂單:", [f"ID {o.id}: {o.weight}t to {o.destination.name}, ${o.revenue}, 截止 {o.deadline}" for o in self.orders])

    def get_action(self):
        """獲取玩家行動"""
        print("\n你想做什麼?")
        print("1. 購買車輛")
        print("2. 接受訂單並調度")
        print("3. 結束天數")
        choice = input("輸入選項 (1-3): ")
        if choice == "1":
            return "buy_vehicle"
        elif choice == "2":
            return "accept_order"
        elif choice == "3":
            return "end_turn"
        else:
            print("無效選項,請重新選擇。")
            return self.get_action()

    def buy_vehicle(self):
        """購買車輛"""
        vtype = input("車輛類型 (Van/Truck): ")
        cost = 5000 if vtype == "Van" else 10000
        if self.money >= cost:
            name = input("車輛名稱: ")
            capacity = 2 if vtype == "Van" else 5
            speed = 60 if vtype == "Van" else 50
            fuel_per_km = 0.5 if vtype == "Van" else 1.0
            vehicle = Vehicle(name, vtype, capacity, speed, fuel_per_km)
            vehicle.location = self.cities["A"]
            self.vehicles.append(vehicle)
            self.money -= cost
            print(f"成功購買 {name},花費 {cost}。")
        else:
            print("資金不足,無法購買車輛。")

    def generate_orders(self):
        """生成隨機訂單"""
        if random.random() < 0.8:  # 80% 機率生成訂單
            order_id = len(self.orders) + 1
            weight = random.randint(1, 5)
            destination = random.choice(list(self.cities.values()))
            revenue = weight * 100
            deadline = random.randint(3, 7)
            self.orders.append(Order(order_id, weight, destination, revenue, deadline))
            print(f"新訂單 ID {order_id}: {weight}t to {destination.name}, ${revenue}, 截止 {deadline} 天。")

    def accept_order(self):
        """接受訂單並調度車輛"""
        if not self.orders:
            print("沒有可用訂單!")
            return
        print("可用訂單:")
        for order in self.orders:
            print(f"ID {order.id}: {order.weight}t to {order.destination.name}, ${order.revenue}, 截止 {order.deadline}")
        try:
            order_id = int(input("輸入訂單 ID: "))
            order = next((o for o in self.orders if o.id == order_id), None)
            if not order:
                print("無效訂單 ID!")
                return
            print("可用車輛:")
            for i, vehicle in enumerate(self.vehicles):
                if vehicle.status == "Idle" and vehicle.capacity >= order.weight:
                    print(f"{i}. {vehicle.name} ({vehicle.type}, 容量 {vehicle.capacity}t)")
            vehicle_idx = int(input("選擇車輛編號: "))
            vehicle = self.vehicles[vehicle_idx]
            if vehicle.status != "Idle" or vehicle.capacity < order.weight:
                print("車輛不可用或容量不足!")
                return
            path, distance = self.dijkstra(vehicle.location, order.destination)
            vehicle.assign_order(order, path)
            self.orders.remove(order)
            fuel_cost = distance * vehicle.fuel_per_km
            self.money -= fuel_cost
            print(f"訂單分配給 {vehicle.name},路徑:{[city.name for city in path]},燃料成本: {fuel_cost}")
        except (ValueError, IndexError):
            print("輸入錯誤,請重試。")

    def end_turn(self):
        """結束當前天數"""
        self.turn += 1
        total_maintenance = sum(v.maintenance_cost for v in self.vehicles)
        if self.money >= total_maintenance:
            self.money -= total_maintenance
            print(f"天數結束,支付維護費用 {total_maintenance}。")
        else:
            print("資金不足,聲譽下降!")
            self.reputation -= 5
        # 更新訂單和車輛
        for order in self.orders[:]:
            order.deadline -= 1
            if order.deadline <= 0:
                print(f"訂單 ID {order.id} 超時,聲譽下降!")
                self.reputation -= 2
                self.orders.remove(order)
        for vehicle in self.vehicles:
            if vehicle.status == "Moving" and vehicle.move():
                self.money += vehicle.current_order.revenue
                self.reputation += 2
                print(f"{vehicle.name} 完成訂單,收入 {vehicle.current_order.revenue},聲譽 +2")
        self.random_event()

    def random_event(self):
        """隨機事件"""
        event = random.choice(["None", "TrafficJam", "VehicleBreakdown"])
        if event == "TrafficJam" and any(v.status == "Moving" for v in self.vehicles):
            vehicle = random.choice([v for v in self.vehicles if v.status == "Moving"])
            print(f"{vehicle.name} 遇到交通堵塞,延誤一天!")
        elif event == "VehicleBreakdown" and self.vehicles:
            vehicle = random.choice(self.vehicles)
            cost = vehicle.maintenance_cost * 2
            if self.money >= cost:
                self.money -= cost
                print(f"{vehicle.name} 發生故障,維修費用 {cost}。")
            else:
                print(f"{vehicle.name} 故障未修,聲譽下降!")
                self.reputation -= 3

    def check_win(self):
        """檢查勝利條件"""
        return self.reputation >= 100

    def check_lose(self):
        """檢查失敗條件"""
        return self.turn > self.max_turns or self.money < 0

説明

  • __init__:初始化城市網絡、車輛、訂單、資金和聲譽。
  • create_cities:構建包含 5 個城市的圖網絡。
  • dijkstra:計算兩城市間最短路徑和距離。
  • run:遊戲主循環,顯示狀態、生成訂單、處理行動。
  • display_state:顯示資金、聲譽、車輛和訂單信息。
  • get_action:提供交互菜單,返回玩家選擇。
  • buy_vehicle:購買小型貨車或大型卡車。
  • generate_orders:隨機生成訂單。
  • accept_order:選擇訂單並分配車輛,計算路線。
  • end_turn:推進天數,更新訂單和車輛狀態,處理隨機事件。
  • random_event:處理交通堵塞或車輛故障。
  • check_win 和 check_lose:檢查聲譽是否達標或遊戲失敗。

2.5 主程序:main.py

啓動遊戲的入口文件。

# main.py
from game import LogisticsGame

if __name__ == "__main__":
    game = LogisticsGame()
    game.run()

運行遊戲

1. 啓動遊戲

在項目目錄下運行:

python main.py

2. 遊戲流程

  1. 初始化
  • 玩家從 10000 資金和 50 聲譽開始,擁有一輛小型貨車。
  1. 每回合行動
  • 顯示狀態:查看資金、聲譽、車輛和訂單。
  • 購買車輛:選擇小型貨車(5000)或大型卡車(10000)。
  • 接受訂單:選擇訂單並分配空閒車輛,計算最短路徑。
  • 結束天數:支付維護費用,更新訂單和車輛狀態,處理隨機事件。
  1. 遊戲結束
  • 聲譽達到 100 勝利,超過 30 天或資金為負失敗。

3. 示例運行

歡迎來到《物流大師》!目標:在 30 天內將聲譽提升到 100。

天數 1
資金: 10000
聲譽: 50
車輛: ['Van1 (Van, Idle, A)']
訂單: []
新訂單 ID 1: 3t to C, \$300, 截止 5 天。

你想做什麼?
1. 購買車輛
2. 接受訂單並調度
3. 結束天數
輸入選項 (1-3): 2

可用訂單:
ID 1: 3t to C, \$300, 截止 5
輸入訂單 ID: 1
可用車輛:
0. Van1 (Van, 容量 2t)
選擇車輛編號: 0
車輛不可用或容量不足!

天數 1
資金: 10000
聲譽: 50
車輛: ['Van1 (Van, Idle, A)']
訂單: ['ID 1: 3t to C, \$300, 截止 5']

你想做什麼?
1. 購買車輛
2. 接受訂單並調度
3. 結束天數
輸入選項 (1-3): 1

車輛類型 (Van/Truck): Truck
車輛名稱: Truck1
成功購買 Truck1,花費 10000。

天數 1
資金: 0
聲譽: 50
車輛: ['Van1 (Van, Idle, A)', 'Truck1 (Truck, Idle, A)']
訂單: ['ID 1: 3t to C, \$300, 截止 5']

你想做什麼?
1. 購買車輛
2. 接受訂單並調度
3. 結束天數
輸入選項 (1-3): 2

可用訂單:
ID 1: 3t to C, \$300, 截止 5
輸入訂單 ID: 1
可用車輛:
0. Van1 (Van, 容量 2t)
1. Truck1 (Truck, 容量 5t)
選擇車輛編號: 1
訂單分配給 Truck1,路徑:['A', 'C'],燃料成本: 150.0

天數 1
資金: -150.0
聲譽: 50
車輛: ['Van1 (Van, Idle, A)', 'Truck1 (Truck, Moving, C)']
訂單: []

你想做什麼?
1. 購買車輛
2. 接受訂單並調度
3. 結束天數
輸入選項 (1-3): 3

天數結束,支付維護費用 300。
遊戲結束。未能在 30 天內達成目標,或資金耗盡。
感謝遊玩《物流大師》!

分析

  • 第一天生成訂單(3 噸到 C),但 Van1 容量不足。
  • 玩家購買 Truck1(10000),分配訂單,燃料成本 150。
  • 結束天數,支付維護費用 300,資金為負,遊戲失敗。

遊戲機制詳解

1. 運輸網絡

  • 城市:5 個城市(A-E),雙向連接,距離 100-200 km。
  • 路徑規劃:Dijkstra 算法計算最短路徑,優化燃料成本。

2. 車輛管理

  • 小型貨車:容量 2 噸,速度 60 km/h,燃料 0.5/公里,維護 100/天。
  • 大型卡車:容量 5 噸,速度 50 km/h,燃料 1.0/公里,維護 200/天。
  • 調度:空閒且容量足夠的車輛可接受訂單。

3. 訂單管理

  • 生成:80% 機率生成訂單,重量 1-5 噸,收益 100/噸,截止 3-7 天。
  • 完成:車輛到達目的地,獲得收益和聲譽。
  • 超時:訂單過期,聲譽 -2。

4. 財務與聲譽

  • 收入:完成訂單獲得收益(100/噸)。
  • 支出:燃料(距離 × 燃料/公里)、維護費用、車輛購買。
  • 聲譽:完成訂單 +2,超時 -2,資金不足 -5。

5. 隨機事件

  • 交通堵塞:運輸中車輛延誤一天。
  • 車輛故障:額外維修費用(維護費 × 2),未修聲譽 -3。

遊戲擴展與優化

當前版本是一個功能完整的物流遊戲原型,我們可以通過以下方式增強遊戲性、技術實現和深度。

1. 遊戲性增強

  • 訂單優先級:引入緊急訂單,收益更高但時間緊。
  • 車輛升級:提升容量或速度,降低燃料消耗。
  • 員工管理:僱傭司機,影響效率和成本。
  • 動態網絡:城市間距離或交通狀況隨時間變化。

示例代碼:緊急訂單

class Order:
    def __init__(self, id, weight, destination, revenue, deadline, priority="normal"):
        self.id = id
        self.weight = weight
        self.destination = destination
        self.revenue = revenue
        self.deadline = deadline
        self.priority = priority  # normal, urgent

class LogisticsGame:
    # ... 其他方法保持不變 ...

    def generate_orders(self):
        if random.random() < 0.8:
            order_id = len(self.orders) + 1
            weight = random.randint(1, 5)
            destination = random.choice(list(self.cities.values()))
            priority = "urgent" if random.random() < 0.3 else "normal"
            revenue = weight * (150 if priority == "urgent" else 100)
            deadline = random.randint(1, 3) if priority == "urgent" else random.randint(3, 7)
            self.orders.append(Order(order_id, weight, destination, revenue, deadline, priority))
            print(f"新訂單 ID {order_id}: {weight}t to {destination.name}, ${revenue}, 截止 {deadline} 天 ({priority})。")

    def end_turn(self):
        self.turn += 1
        total_maintenance = sum(v.maintenance_cost for v in self.vehicles)
        if self.money >= total_maintenance:
            self.money -= total_maintenance
            print(f"天數結束,支付維護費用 {total_maintenance}。")
        else:
            print("資金不足,聲譽下降!")
            self.reputation -= 5
        for order in self.orders[:]:
            order.deadline -= 1
            if order.deadline <= 0:
                penalty = 5 if order.priority == "urgent" else 2
                print(f"訂單 ID {order.id} 超時,聲譽 -{penalty}!")
                self.reputation -= penalty
                self.orders.remove(order)
        for vehicle in self.vehicles:
            if vehicle.status == "Moving" and vehicle.move():
                revenue = vehicle.current_order.revenue
                reputation_gain = 3 if vehicle.current_order.priority == "urgent" else 2
                self.money += revenue
                self.reputation += reputation_gain
                print(f"{vehicle.name} 完成訂單,收入 {revenue},聲譽 +{reputation_gain}")
        self.random_event()

實現效果

  • 30% 機率生成緊急訂單,收益 150/噸,截止時間 1-3 天。
  • 緊急訂單超時聲譽 -5,完成聲譽 +3,增加策略性。

2. 技術優化

  • 圖形界面:使用 Pygame 顯示城市網絡、車輛移動和訂單狀態。
  • 存檔功能:保存遊戲狀態到文件。
  • 路徑可視化:繪製最短路徑和車輛動畫。

示例代碼:Pygame 界面

import pygame

class LogisticsGameWithGUI(LogisticsGame):
    def __init__(self):
        super().__init__()
        pygame.init()
        self.screen = pygame.display.set_mode((800, 600))
        pygame.display.set_caption("物流大師")
        self.font = pygame.font.Font(None, 36)
        self.city_positions = {
            "A": (100, 100), "B": (300, 100), "C": (300, 300),
            "D": (500, 300), "E": (500, 100)
        }

    def display_state(self):
        """使用 Pygame 顯示狀態"""
        self.screen.fill((255, 255, 255))
        # 繪製城市連接
        for city in self.cities.values():
            for neighbor, distance in city.neighbors.items():
                start = self.city_positions[city.name]
                end = self.city_positions[neighbor.name]
                pygame.draw.line(self.screen, (0, 0, 0), start, end, 2)
                mid = ((start[0] + end[0]) // 2, (start[1] + end[1]) // 2)
                text = self.font.render(str(distance), True, (0, 0, 0))
                self.screen.blit(text, mid)
        # 繪製城市
        for name, pos in self.city_positions.items():
            pygame.draw.circle(self.screen, (0, 0, 255), pos, 20)
            text = self.font.render(name, True, (0, 0, 0))
            self.screen.blit(text, (pos[0] - 10, pos[1] - 30))
        # 繪製車輛
        for vehicle in self.vehicles:
            pos = self.city_positions[vehicle.location.name]
            color = (255, 0, 0) if vehicle.status == "Idle" else (0, 255, 0)
            pygame.draw.circle(self.screen, color, pos, 10)
        # 顯示狀態
        texts = [
            f"天數: {self.turn + 1}",
            f"資金: {self.money}",
            f"聲譽: {self.reputation}",
            f"車輛: {[f'{v.name} ({v.status})' for v in self.vehicles]}"
        ]
        for i, text in enumerate(texts):
            rendered = self.font.render(text, True, (0, 0, 0))
            self.screen.blit(rendered, (10, 400 + i * 30))
        pygame.display.flip()
        super().display_state()

    def run(self):
        """運行遊戲,整合 Pygame 事件"""
        print("歡迎來到《物流大師》!")
        running = True
        while running and not self.game_over:
            self.display_state()
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
            action = self.get_action()
            if action == "buy_vehicle":
                self.buy_vehicle()
            elif action == "accept_order":
                self.accept_order()
            elif action == "end_turn":
                self.end_turn()
            if self.check_win():
                print("恭喜!你成功將聲譽提升到 100!")
                self.game_over = True
            if self.check_lose():
                print("遊戲結束。未能在 30 天內達成目標,或資金耗盡。")
                self.game_over = True
            time.sleep(1)
        pygame.quit()

實現效果

  • 顯示城市網絡(節點為城市,邊為距離),車輛為紅點(空閒)或綠點(運輸中)。
  • CLI 交互保留,狀態在屏幕底部顯示。

3. 策略深度

  • 多車輛調度:支持同時調度多輛車執行多個訂單。
  • 動態成本:燃料價格隨市場波動。
  • 客户關係:完成訂單提升客户忠誠度,解鎖高價值訂單。

示例代碼:多車輛調度

class LogisticsGame:
    # ... 其他方法保持不變 ...

    def accept_order(self):
        if not self.orders:
            print("沒有可用訂單!")
            return
        print("可用訂單:")
        for order in self.orders:
            print(f"ID {order.id}: {order.weight}t to {order.destination.name}, ${order.revenue}, 截止 {order.deadline}")
        try:
            order_ids = input("輸入訂單 ID(多個用空格分隔): ").split()
            assignments = []
            for order_id in order_ids:
                order = next((o for o in self.orders if o.id == int(order_id)), None)
                if not order:
                    print(f"無效訂單 ID {order_id}!")
                    continue
                print(f"\n為訂單 ID {order_id} 選擇車輛:")
                available_vehicles = [i for i, v in enumerate(self.vehicles) if v.status == "Idle" and v.capacity >= order.weight]
                for i in available_vehicles:
                    print(f"{i}. {self.vehicles[i].name} ({self.vehicles[i].type}, 容量 {self.vehicles[i].capacity}t)")
                vehicle_idx = int(input("選擇車輛編號(-1 跳過): "))
                if vehicle_idx == -1:
                    continue
                vehicle = self.vehicles[vehicle_idx]
                if vehicle_idx not in available_vehicles:
                    print("車輛不可用或容量不足!")
                    continue
                assignments.append((order, vehicle))
            for order, vehicle in assignments:
                path, distance = self.dijkstra(vehicle.location, order.destination)
                vehicle.assign_order(order, path)
                self.orders.remove(order)
                fuel_cost = distance * vehicle.fuel_per_km
                self.money -= fuel_cost
                print(f"訂單 ID {order.id} 分配給 {vehicle.name},路徑:{[city.name for city in path]},燃料成本: {fuel_cost}")
        except (ValueError, IndexError):
            print("輸入錯誤,請重試。")

實現效果

  • 玩家可同時為多個訂單分配車輛,提升效率。

遊戲策略與玩法分析

1. 玩家策略

  • 路線優化:選擇最短路徑減少燃料成本。
  • 車輛選擇:根據訂單重量選擇合適車輛。
  • 時間管理:優先處理緊急訂單,避免超時。
  • 資源平衡:控制車輛數量,平衡維護費用和收入。

2. 平衡性

  • 初始資源:10000 資金和 1 輛貨車,適合初期運營。
  • 聲譽增長:每次完成訂單 +2(緊急 +3),需 50-75 次完成達標。
  • 回合限制:30 天需高效調度。
  • 成本與收入:燃料、維護費用與訂單收益需平衡。

3. 重玩價值

  • 嘗試不同車輛組合和調度策略。
  • 應對隨機事件,調整運營計劃。
  • 優化資源管理,實現更高聲譽。

附錄:完整代碼

以下是整合後的完整代碼,分為 game.pymain.py,可直接運行。

game.py

import random
import time
import heapq

class City:
    def __init__(self, name):
        self.name = name
        self.neighbors = {}

    def add_neighbor(self, city, distance):
        self.neighbors[city] = distance

class Vehicle:
    def __init__(self, name, vtype, capacity, speed, fuel_per_km):
        self.name = name
        self.type = vtype
        self.capacity = capacity
        self.speed = speed
        self.fuel_per_km = fuel_per_km
        self.location = None
        self.current_order = None
        self.maintenance_cost = 100 if vtype == "Van" else 200
        self.status = "Idle"
        self.path = []
        self.current_step = 0

    def assign_order(self, order, path):
        self.current_order = order
        self.status = "Moving"
        self.path = path
        self.current_step = 0

    def move(self):
        if self.status == "Moving" and self.current_step < len(self.path) - 1:
            self.current_step += 1
            self.location = self.path[self.current_step]
            return False
        elif self.status == "Moving":
            self.status = "Idle"
            self.current_order = None
            return True
        return False

class Order:
    def __init__(self, id, weight, destination, revenue, deadline, priority="normal"):
        self.id = id
        self.weight = weight
        self.destination = destination
        self.revenue = revenue
        self.deadline = deadline
        self.priority = priority

class LogisticsGame:
    def __init__(self):
        self.cities = self.create_cities()
        self.vehicles = []
        self.orders = []
        self.money = 10000
        self.reputation = 50
        self.turn = 0
        self.max_turns = 30
        self.game_over = False

    def create_cities(self):
        cities = {name: City(name) for name in ["A", "B", "C", "D", "E"]}
        connections = [
            ("A", "B", 100), ("A", "C", 150), ("B", "C", 120),
            ("B", "D", 200), ("C", "D", 180), ("C", "E", 160),
            ("D", "E", 140)
        ]
        for city1, city2, distance in connections:
            cities[city1].add_neighbor(cities[city2], distance)
            cities[city2].add_neighbor(cities[city1], distance)
        return cities

    def dijkstra(self, start, end):
        distances = {city: float("inf") for city in self.cities}
        distances[start] = 0
        pq = [(0, start)]
        predecessors = {city: None for city in self.cities}
        while pq:
            current_distance, current_city = heapq.heappop(pq)
            if current_city == end:
                break
            if current_distance > distances[current_city]:
                continue
            for neighbor, distance in current_city.neighbors.items():
                distance_to_neighbor = current_distance + distance
                if distance_to_neighbor < distances[neighbor]:
                    distances[neighbor] = distance_to_neighbor
                    predecessors[neighbor] = current_city
                    heapq.heappush(pq, (distance_to_neighbor, neighbor))
        path = []
        current = end
        while current:
            path.append(current)
            current = predecessors[current]
        return path[::-1], distances[end]

    def run(self):
        print("歡迎來到《物流大師》!目標:在 30 天內將聲譽提升到 100。")
        self.vehicles.append(Vehicle("Van1", "Van", 2, 60, 0.5))
        self.vehicles[0].location = self.cities["A"]
        while not self.game_over:
            self.display_state()
            self.generate_orders()
            action = self.get_action()
            if action == "buy_vehicle":
                self.buy_vehicle()
            elif action == "accept_order":
                self.accept_order()
            elif action == "end_turn":
                self.end_turn()
            if self.check_win():
                print("恭喜!你成功將聲譽提升到 100!")
                self.game_over = True
            if self.check_lose():
                print("遊戲結束。未能在 30 天內達成目標,或資金耗盡。")
                self.game_over = True
            time.sleep(1)

    def display_state(self):
        print(f"\n天數 {self.turn + 1}")
        print(f"資金: {self.money}")
        print(f"聲譽: {self.reputation}")
        print("車輛:", [f"{v.name} ({v.type}, {v.status}, {v.location.name})" for v in self.vehicles])
        print("訂單:", [f"ID {o.id}: {o.weight}t to {o.destination.name}, ${o.revenue}, 截止 {o.deadline} ({o.priority})" for o in self.orders])

    def get_action(self):
        print("\n你想做什麼?")
        print("1. 購買車輛")
        print("2. 接受訂單並調度")
        print("3. 結束天數")
        choice = input("輸入選項 (1-3): ")
        if choice == "1":
            return "buy_vehicle"
        elif choice == "2":
            return "accept_order"
        elif choice == "3":
            return "end_turn"
        else:
            print("無效選項,請重新選擇。")
            return self.get_action()

    def buy_vehicle(self):
        vtype = input("車輛類型 (Van/Truck): ")
        cost = 5000 if vtype == "Van" else 10000
        if self.money >= cost:
            name = input("車輛名稱: ")
            capacity = 2 if vtype == "Van" else 5
            speed = 60 if vtype == "Van" else 50
            fuel_per_km = 0.5 if vtype == "Van" else 1.0
            vehicle = Vehicle(name, vtype, capacity, speed, fuel_per_km)
            vehicle.location = self.cities["A"]
            self.vehicles.append(vehicle)
            self.money -= cost
            print(f"成功購買 {name},花費 {cost}。")
        else:
            print("資金不足,無法購買車輛。")

    def generate_orders(self):
        if random.random() < 0.8:
            order_id = len(self.orders) + 1
            weight = random.randint(1, 5)
            destination = random.choice(list(self.cities.values()))
            priority = "urgent" if random.random() < 0.3 else "normal"
            revenue = weight * (150 if priority == "urgent" else 100)
            deadline = random.randint(1, 3) if priority == "urgent" else random.randint(3, 7)
            self.orders.append(Order(order_id, weight, destination, revenue, deadline, priority))
            print(f"新訂單 ID {order_id}: {weight}t to {destination.name}, ${revenue}, 截止 {deadline} 天 ({priority})。")

    def accept_order(self):
        if not self.orders:
            print("沒有可用訂單!")
            return
        print("可用訂單:")
        for order in self.orders:
            print(f"ID {order.id}: {order.weight}t to {order.destination.name}, ${order.revenue}, 截止 {order.deadline} ({order.priority})")
        try:
            order_ids = input("輸入訂單 ID(多個用空格分隔): ").split()
            assignments = []
            for order_id in order_ids:
                order = next((o for o in self.orders if o.id == int(order_id)), None)
                if not order:
                    print(f"無效訂單 ID {order_id}!")
                    continue
                print(f"\n為訂單 ID {order_id} 選擇車輛:")
                available_vehicles = [i for i, v in enumerate(self.vehicles) if v.status == "Idle" and v.capacity >= order.weight]
                for i in available_vehicles:
                    print(f"{i}. {self.vehicles[i].name} ({self.vehicles[i].type}, 容量 {self.vehicles[i].capacity}t)")
                vehicle_idx = int(input("選擇車輛編號(-1 跳過): "))
                if vehicle_idx == -1:
                    continue
                vehicle = self.vehicles[vehicle_idx]
                if vehicle_idx not in available_vehicles:
                    print("車輛不可用或容量不足!")
                    continue
                assignments.append((order, vehicle))
            for order, vehicle in assignments:
                path, distance = self.dijkstra(vehicle.location, order.destination)
                vehicle.assign_order(order, path)
                self.orders.remove(order)
                fuel_cost = distance * vehicle.fuel_per_km
                self.money -= fuel_cost
                print(f"訂單 ID {order.id} 分配給 {vehicle.name},路徑:{[city.name for city in path]},燃料成本: {fuel_cost}")
        except (ValueError, IndexError):
            print("輸入錯誤,請重試。")

    def end_turn(self):
        self.turn += 1
        total_maintenance = sum(v.maintenance_cost for v in self.vehicles)
        if self.money >= total_maintenance:
            self.money -= total_maintenance
            print(f"天數結束,支付維護費用 {total_maintenance}。")
        else:
            print("資金不足,聲譽下降!")
            self.reputation -= 5
        for order in self.orders[:]:
            order.deadline -= 1
            if order.deadline <= 0:
                penalty = 5 if order.priority == "urgent" else 2
                print(f"訂單 ID {order.id} 超時,聲譽 -{penalty}!")
                self.reputation -= penalty
                self.orders.remove(order)
        for vehicle in self.vehicles:
            if vehicle.status == "Moving" and vehicle.move():
                revenue = vehicle.current_order.revenue
                reputation_gain = 3 if vehicle.current_order.priority == "urgent" else 2
                self.money += revenue
                self.reputation += reputation_gain
                print(f"{vehicle.name} 完成訂單,收入 {revenue},聲譽 +{reputation_gain}")
        self.random_event()

    def random_event(self):
        event = random.choice(["None", "TrafficJam", "VehicleBreakdown"])
        if event == "TrafficJam" and any(v.status == "Moving" for v in self.vehicles):
            vehicle = random.choice([v for v in self.vehicles if v.status == "Moving"])
            print(f"{vehicle.name} 遇到交通堵塞,延誤一天!")
        elif event == "VehicleBreakdown" and self.vehicles:
            vehicle = random.choice(self.vehicles)
            cost = vehicle.maintenance_cost * 2
            if self.money >= cost:
                self.money -= cost
                print(f"{vehicle.name} 發生故障,維修費用 {cost}。")
            else:
                print(f"{vehicle.name} 故障未修,聲譽下降!")
                self.reputation -= 3

    def check_win(self):
        return self.reputation >= 100

    def check_lose(self):
        return self.turn > self.max_turns or self.money < 0

main.py

from game import LogisticsGame

if __name__ == "__main__":
    game = LogisticsGame()
    game.run()