1. 程式人生 > >PyGmae:有限狀態機實踐(九)

PyGmae:有限狀態機實踐(九)

#   _*_ coding:utf-8 _*_
from random import randint
import sys
import pygame
from pygame.locals import *
from gameobjects.vector2 import Vector2
import time

__author__ = 'admin'

'''
    螞蟻狀態機(九)
    新目標:增加蜘蛛物件
    1.蜘蛛滿屏遊走
    2.若蜘蛛走進巢穴
    ExploreState中判斷葉子的同時也判斷蜘蛛,再把蜘蛛id繫結到ant物件上即可
    再實現蜘蛛的explore
    想要蜘蛛和ant都共享這個explore的方法,只能將ant的explore方法移至ObjProduct中
    再在Microcosm中增加遍歷所有物件的並使其explore的方法
    還需再處理葉子沒有explore的問題
    在狀態機管理器StateMachine中將obj.name為'leaf'的過濾掉就可以了
    問題:怎麼才能使得蜘蛛擁有wait()而不擁有seek等狀態
    解決:使用了最不想用的辦法,蜘蛛另存一個狀態集合,在蜘蛛和螞蟻共享的狀態機上加以判斷當前物件是螞蟻還是蜘蛛
'''
SCREEN_SIZE = (640, 480)
NEST_POSITION = (320, 240)
NEST_SIZE = 100.

WHITE = (255, 255, 255)

clock = pygame.time.Clock()


class Microcosm(object):
    def __init__(self):
        self.objects_dict = {}
        self.capricious_dict = {}
        self.obj_id = 0
        self.capricious_list = []

    def addObj(self, obj):
        #   在changeState方法中需要傳入一個包含所有螞蟻物件的列表,這個列表中也包含有未被拾取的葉子
        self.capricious_list.append(obj)
        #   用於儲存程式執行之後生成的所有物件及其對應的id
        self.objects_dict[self.obj_id] = obj
        #   道理同objects_dict,只不過該dict會不斷的被新增和刪除,主要是確定哪些葉子允許被螞蟻seek
        self.capricious_dict[self.obj_id] = obj
        #   是在這個位置為物件附上id屬性的,下面刪除字典元素時要用到這個id
        obj.id = self.obj_id
        self.obj_id += 1

    def getObj(self, obj_id):
        return self.objects_dict[obj_id]

    def get_closeTo_obj(self, name, position, range=50):
        for obj in self.capricious_dict.values():
            if obj.name == name:
                distance = position.get_distance_to(obj.position)
                if distance <= range:
                    return obj
        return None

    #   繪製方法存在這裡,每個繼承的子類都在這裡繪製,無需在每個物件類中再繪製了
    #   每個物件類中只需要實時的變更其位置即可
    def draw(self, surface):
        surface.fill(WHITE)
        pygame.draw.circle(surface, (200, 255, 200), NEST_POSITION, int(NEST_SIZE))
        for obj in self.objects_dict.values():
            obj.draw(surface)

class ObjProduct(object):
    def __init__(self, microcosm, name, path):
        self.microcosm = microcosm
        self.name = name
        self.image_path = path
        self.image = pygame.image.load(self.image_path).convert_alpha()
        self.state = 0
        self.fsm = None
        self.id = 0
        self.speed = 0
        self.position = Vector2(0, 0)
        self.destination = Vector2(0, 0)

    def bind(self, state, fsm):
        self.fsm = fsm
        self.state = state

    def draw(self, surface):
        x, y = self.position
        w, h = self.image.get_size()
        surface.blit(self.image, (x - w / 2, y - h / 2))

    def explore(self):
        print("[%s-%d]探索中....." % (self.name, self.id))
        distance = self.destination - self.position
        #   獲取此時螞蟻距離目標點間的向量長度(即兩點間的實際距離)
        x = self.position.get_distance_to(self.destination)
        #   獲取單位向量,即每次螞蟻移動的向量
        heading = distance.normalise()
        #   如果螞蟻再移動單位向量就會越過目標點
        if x <= heading.get_length():
            print("[%s-%d]到達目的地了....前往下一個點" % (self.name, self.id))
            # self.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
            return True
        else:
            self.position += heading

class Spider(ObjProduct):
    def __init__(self, microcosm, name, path):
        ObjProduct.__init__(self, microcosm, name, path)
        self.position = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))

    def draw(self, surface):
        ObjProduct.draw(self, surface)

    def wait(self):
        print("[%s-%d]停頓下,思考人生....." % (self.name, self.id))
        pygame.time.delay(10)

class Leaf(ObjProduct):
    def __init__(self, microcosm, name, path):
        ObjProduct.__init__(self, microcosm, name, path)
        self.position = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))

    def draw(self, surface):
        ObjProduct.draw(self, surface)

class Ant(ObjProduct):
    def __init__(self, microcosm, name, path):
        ObjProduct.__init__(self, microcosm, name, path)
        self.position = Vector2(100, 100)
        self.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
        #   這裡讓leaf_id為None,單純就是不想給它一個數字而已,避免這個預設值作為Key使用時報錯
        self.leaf_id = None

    def draw(self, surface):
        ObjProduct.draw(self, surface)

    def wait(self):
        print("[%s-%d]停頓下,思考人生....." % (self.name, self.id))
        pygame.time.delay(10)

class State(object):
    def exec(self, obj):
        pass

    def exit(self, obj):
        pass


class ExploreState(State):
    def exec(self, obj):
        #   決定探索過程中觸發wait的頻率
        if randint(1, 1000) == 1:
            #   訊號置為0
            obj.state = 0
        else:
            if obj.name == 'ant':
                #   判斷當前螞蟻物件附近範圍內是否有葉子存在,並獲取這個葉子物件
                leaf_ = obj.microcosm.get_closeTo_obj('leaf', obj.position)
                #   如果有這樣的葉子物件
                if leaf_ is not None:
                    #   為當前螞蟻物件新增該目標葉子id屬性(將該葉子繫結到該螞蟻上,方便呼叫)
                    obj.leaf_id = leaf_.id
                    #   訊號置為seek
                    obj.state = 2
                else:
                    #   附近沒有葉子就執行explore
                    if obj.explore():
                        obj.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
            elif obj.name == 'spider':
                if obj.explore():
                    obj.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))


class WaitSate(State):
    def exec(self, obj):
        #   執行wait方法
        obj.wait()
        #   訊號置為1,使得螞蟻再動起來
        obj.state = 1


class SeekState(State):
    def exec(self, obj):
        #   當前螞蟻物件是否綁定了leaf_id屬性(這點可以說明螞蟻附近是否有葉子)
        if obj.leaf_id is not None:
            #   當前螞蟻已經有了葉子目標,capricious_dict中該葉子還未被刪除,說明未被其他螞蟻搶先拾取
            if obj.leaf_id in obj.microcosm.capricious_dict.keys():
                print("[%s-%d]發現目標葉子..走過去" % (obj.name, obj.id))
                #   通過已知的葉子id獲取對應的葉子物件
                leaf_ = obj.microcosm.getObj(obj.leaf_id)
                #   將葉子的位置當做螞蟻本次行為的終點
                obj.destination = leaf_.position
                if obj.explore():
                    print("[%s-%d]已達到目標葉子" % (obj.name, obj.id))
                    #   將該葉子移出capricious_dict中,起到不可被seek的作用
                    obj.microcosm.capricious_dict.pop(leaf_.id)
                    obj.microcosm.capricious_list.remove(leaf_)
                    #   將巢穴範圍內隨機的一個位置當做螞蟻TransState的終點
                    obj.destination = Vector2(NEST_POSITION) + Vector2(randint(-40, 40), randint(-40, 40))
                    obj.state = 3
            else:
                #   當前螞蟻鎖定的葉子被其他螞蟻搶先拾取
                print("[%s-%d]的葉子被別的螞蟻搶先拾取...." % (obj.name, obj.id))
                obj.state = 1
        else:
            #   當前螞蟻附近沒有葉子了,執行探索
            obj.state = 1

class TransState(State):
    def exec(self, obj):
        leaf_ = obj.microcosm.getObj(obj.leaf_id)
        print("[%s-%d]拾起葉子,走回巢穴..." % (obj.name, obj.id))
        #   通過螞蟻當前的位置確定其拾取到的葉子的位置-----》待優化
        leaf_.position = Vector2(obj.position)
        #   獲取此時螞蟻起點->終點的向量
        distance = obj.destination - obj.position
        #   獲取此時螞蟻距離目標點間的向量長度(即兩點間的實際距離)
        x = obj.position.get_distance_to(obj.destination)
        #   獲取單位向量,即每次螞蟻移動的向量
        heading = distance.normalise()
        #   如果螞蟻再移動單位向量就會越過目標點
        if x <= heading.get_length():
            # print("[Ant-%d]已回到巢穴之中" % obj.id)
            #   將該螞蟻繫結的葉子id置為None
            obj.leaf_id = None
            #   訊號置為1,繼續探索
            obj.state = 1
        else:
            obj.position += heading
            #   還未將葉子運回巢穴之內,繼續trans
            obj.state = 3

class StateMachine(object):
    def __init__(self):
        #   狀態集合
        self.states_Ant = {0: WaitSate(), 1: ExploreState(), 2: SeekState(), 3: TransState()}
        self.states_Spider = {0: WaitSate(), 1: ExploreState()}

    #   改變狀態
    def changeState(self, objs):
        for obj in objs:
            #   這裡先留著吧,考慮下state是否有為None的必要
            if obj.state is None:
                return
            else:
                if obj.name == 'leaf':
                    #   無狀態,不做處理,其實也可以在外層先過濾掉leaf物件
                    pass
                elif obj.name == 'spider':
                    print("name[%s]--state[%d]" % (obj.name, obj.state))
                    newFsm = self.states_Spider[obj.state]
                    newFsm.exec(obj)
                else:
                    print("name[%s]--state[%d]" % (obj.name, obj.state))
                    newFsm = self.states_Ant[obj.state]
                    newFsm.exec(obj)

def checkForOut():
    for event in pygame.event.get():
        if event.type == 12:
            sys.exit()
        if event.type == 2:
            if event.key == 27:
                exit()


pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)

micr = Microcosm()
sm = StateMachine()
for i in range(3):
    ant = Ant(micr, 'ant', r"E:\PycharmProjects\PGStudy\resource\ant.png")
    ant.bind(0, sm.states_Ant[0])
    micr.addObj(ant)

for i in range(1):
    spider = Spider(micr, 'spider', r"E:\PycharmProjects\PGStudy\resource\spider.png")
    micr.addObj(spider)

while True:
    checkForOut()
    #   生出葉子
    if randint(1, 100) == 1:
        leaf = Leaf(micr, 'leaf', r"E:\PycharmProjects\PGStudy\resource\leaf.png")
        micr.addObj(leaf)
    #   將繪製方法全部封裝,每次迴圈重刷螢幕上所有物件
    micr.draw(screen)
    sm.changeState(micr.capricious_list)
    pygame.display.update()
    pygame.time.delay(10)

相關推薦

PyGmae有限狀態實踐()

# _*_ coding:utf-8 _*_ from random import randint import sys import pygame from pygame.locals import * from gameobjects.vector2 import V

PyGmae有限狀態實踐(三)

# _*_ coding:utf-8 _*_ from random import randint import sys import pygame from pygame.locals import * from gameobjects.vector2 import V

Python有限狀態小用例

# _*_ coding:utf-8 _*_ from random import randint from time import sleep __author__ = 'admin' ''' 23種設計模式之狀態模式:有限狀態機 下面介紹了燈的三種

詞法分析從RE(正則表示式)到DFA(確定的有限狀態

模式識別(Pattern recognization)是現在非常流行的一個詞,我們對詞法的分析也是基於模式(pattern-based)的。我們用正則表示式(Regular Expression)來定義單詞的模式,而在詞法分析時,有限狀態機(Finite Aut

最佳實踐有限狀態

有限狀態機(Finite State Machine,FSM),簡稱狀態機。今天這篇文件的主體思路,來自本人授權的一項發明專利。第一次嘗試寫出來,希望分享給更多人。 我當時寫這個專利的時候,太有感覺了。非常的激動,同時我也很想分享給同事,但是可能太抽象了,未果。然後我想申請優秀專利獎,沒有渠道!所以最近刷屏的

獨立項目-角色控制器-有限狀態(FSM)

技術分享 OS pos .com com 過渡 動畫過渡 unity 狀態機   學習內容:     Unity5的新動畫系統       1.動畫導入 分動畫       2.創建動畫狀態機       3.設置動畫過渡 設置動畫跳轉條件       4.動畫重定向 獨立

go - FSM(有限狀態)初體驗

層次 time lba 當前 時機 警告 pen nil 自定義 FSM有限狀態機 /** * @Author: wsp * @Time : 2018/4/11 16:45 * @Description: */ package fsmStrudy import

Verilog筆記.3.有限狀態

情況 || mage 參數 lose default def ril 定義 有限狀態機有限狀態機是由寄存器組和組合邏輯構成的硬件時序電路,其狀態(即由寄存器組的1和0的組合狀態所構成的有限個狀態)只可能在同一時鐘跳變沿的情況下才能從一個狀態轉向另一個狀態,究竟轉向哪一狀態還

TCP三次握手、四次端口和有限狀態

TCP三次握手、四次端口和有限狀態機1、TCP用三次握手(three-way handshake) 一對終端同時初始化一個它們之間的連接是可能的。但通常是由一端打開一個套接字(socket)然後監聽來自另一方的連接,這就是通常所指的被動打開(passive open)。服務器端被被動打開以後,用戶端就能開始創

FPGA學習筆記(七)——FSM(Finite State Machine,有限狀態)設計

fault mil 系統 time 編碼 代碼 ril esc 寫法   FPGA設計中,最重要的設計思想就是狀態機的設計思想!狀態機的本質就是對具有邏輯順序和時序規律的事件的一種描述方法,它有三個要素:狀態、輸入、輸出:狀態也叫做狀態變量(比如可以用電機的不同轉速作為狀態

文字版 描述TCP三次握手和四次揮手以及有限狀態

切換 list 遠方 是什麽 int last 關閉 ive tcp報文 客戶端和服務器 ,雙方都處於第一次交互的情況下展開通信 三次握手 1.首先 服務器 需要是處於listen收聽狀態下才能接受報文客戶端由closed狀態 打開並向服務器發送報文SYN=1 申請建

從React Redux的實際業務場景來看有限狀態

寫在前面 上一篇:從Promise的實現來看有限狀態機 上一篇講到了一個簡單的,利用了有限狀態機的前端實現Promise。Promise的有限狀態機除了start以及finish兩個狀態,其核心的三個狀態其實就是一個非同步行為的三種狀態:PENDING、FULFILLED、REJECTED。通過非同步行為

前端狀態管理與有限狀態

原文連結 當下前端流行的框架,都是用狀態來描述介面(state => view),可以說前端開發實際上就是在維護各種狀態(state),這已經成為目前前端開發的共識。 View = ViewModel(Model); 複製程式碼 理想情況下,ViewModel 是純函式,給定相同的 Mod

Verilog_有限狀態

名詞解釋 狀態機就是一種能夠描述具有邏輯順序和時序順序事件的方法。 狀態機有兩大類:Mealy型和Moore型。 Moore型狀態機的輸出只與當前狀態有關,而Mealy型狀態機的輸出不僅取決於當前狀態,還受到輸入的直接控制,並且可能與狀態無關。 當使用Veril

有限狀態(FSM)的設計

有限狀態機(FSM)的設計_zhangxianhe  有限狀態機(FSM)是一種常見的電路,由時序電路和組合電路組成。  設計有限狀態機的第一步是確定採用Moore 狀態機還是採用Mealy 狀態機。 Mealy 型:狀態的轉變不僅和當前狀態有關,而且跟各輸入訊號有關; Moo

Unity簡單有限狀態實現

【前言】 本篇來說一下關於簡單有限狀態機的演算法實現,當然我們的幾個狀態會比較簡單,而且本身我也是處於入門狀態,所以當成一個簡單的知識積累。有限狀態機,顧名思義,狀態是有限個的,而且狀態之間是關聯的,本篇寫的狀態機,其實是類似寫遊戲裡面的AI機器人,就是那些遊戲

Unity3d 有限狀態

using System.Collections; using System.Collections.Generic; using UnityEngine; //裝換條件 public enum Transition { NullTransition=0, SeePlayer, LostPlaye

有限狀態(FSM)寫法的個人總結(一段式,二段式,三段式)

      狀態機描述時關鍵是要描述清楚幾個狀態機的要素,即如何進行狀態轉移,每個狀態的輸出是什麼,狀態轉移的條件等。具體描述時方法各種各樣,最常見的有三種描述方式:      (1)一段式:整個狀態機寫到一個always模組裡面,在該模組中既描述狀態轉移,又描述狀態的輸入

基於委託與事件的有限狀態設計與實現(Unity3d)

有限狀態機設計與實現 前言 最近看到一句話,就是優秀是一種習慣,所以突然就總想寫點什麼,當作是遊戲開發這條路上的學習筆記吧,另外也時刻提醒自己需要不斷努力。 什麼是狀態機? 先貼百度百科的概念內容吧: 有限狀態機,(英語:Finite-state machine

譯文|GAMEMAKER STUDIO 系列簡單狀態

按照設計,狀態機一次只能處於一種狀態。 由我們來定義對我們的情況有意義的狀態,以及它們之間的關係。 在本文中,我們將使用狀態機來控制在任何給定時間可用的玩家操作,允許我們設定角色並定義角色可以執行的操作。 大家好, 今天我想告訴你如何設定一個簡單的狀態機。 狀態機是一種資料結構,顧名思義,它跟蹤不同的狀