分類: 3C資訊

  • 中國發改委協調汽車充電設施建設 加快政策落實

    中國發改委協調汽車充電設施建設 加快政策落實

    從中國發改委網站獲悉,停車場充電設施建設協調會7月18日下午召開。會議表示將針對大城市、特大城市存在的停車位資源緊張、社會停車場投資主體多、充電設施企業盈利模式相對單一等問題,進一步推進相關工作。  
      停車場充電設施建設協調會由中國國家發改委基礎產業司副司長鄭劍主持,就2016年第二批城市停車場項目配建充電基礎設施問題,與安徽、江蘇、江西、陝西、浙江、湖北、上海、大連、廈門等地方發展改革委、充電基礎設施服務企業和國家電網公司進行交流座談。   據國家能源局電力司初步統計,截至今年6月底,中國全國已建成公共充電樁8.1萬個,比去年底增長65%;隨車建成私人充電樁超過5萬個,比去年底增長約12%。1-6月全國新能源汽車充電量超過6億kWh,替代燃油約20萬噸,電動汽車的發展對能源結構調整和城市環境提升貢獻明顯。   為新能源汽車的推廣和應用創造良好的環境,國家能源局相關部門加快了推動充電樁政策規劃的落實,組織起草加快居民區充電基礎設施建設的檔。該文件有望7月份出臺,將有效推進居民區和工作場所建樁工作,合理優化公共充電樁佈局,提高公共充電樁利用率。   文章來源:中國發展網

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 最強新能源汽車 奧迪發出核動力超跑

    最強新能源汽車 奧迪發出核動力超跑

    為什麼奧迪MesarthimF-Tron Quattro是最強新能源汽車?因為它的複雜程度,已經超出了你的物理知識認知。如果對新能源汽車大概分個等級,那麼“二次電池”驅動的純電動車是一級,插電式混合動力是二級,太陽能汽車三級,燃料電池汽車四級,那麼最強的是?當然是核動力汽車。  
      據稱,奧迪未來的超級跑車即是一款核動力汽車,代號Mesarthim F-Tron Quattro,由俄羅斯工程師操刀設計。外型前衛十足,頗有蝙蝠車的味道。   核動力主要部件核反應爐與離子發射器位於前後軸之間,旁邊是發熱裝置,產生的蒸汽進而驅動電機發電,動力電池安裝在前艙,使用四個輪轂電機驅動車輪前進,同時還有電機驅動離子發射器、冷凝器等,除了核燃料供給,其它整個系統構成一個閉環生態。  
      由於動力系統是個獨立部分,導致其它部件的安裝位置是個問題,為解決這個問題,工程師設計了一套叫做“Solid Cage”的獨立底盤,這個底盤是聚合物材料,可以3D列印出來,而且可以獨立拆卸。   除此以外,底盤上還有一個平底罐,裡面裝有磁流體,在有磁性的路面上行駛時,車子就會產生下壓力,過彎的時候能抵制側傾力,直行的時候抑制抬頭點頭。   雖然奧迪暫時沒有給出這款車具體推出時間表,總體來說還是值得稱讚的。   文章來源:蓋世汽車

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

  • 新能源積分交易制度 或替代財政補貼

    新能源積分交易制度 或替代財政補貼

    新能源汽車業發展日益成熟之時,單純的財政補貼帶來的車企騙補等諸多負面效應逐漸顯現。對此,有專家提出建議採用積分交易機制替代財政補貼政策,促進車企在傳統燃油車節能和新能源車技術進步兩方面共同發展。  
      中國汽車技術研究中心新能源汽車積分政策負責人時間表示,相比真金白銀的財政補貼,新能源積分交易制度靈活性更高,是當下國家推動新能源產業發展的一種可行方式。   新能源積分交易制度,即政府將企業年度“零排放”車型的銷售情況記錄成積分,以積分為依據來考核企業在節能減排方面是否達標。若企業積分不達標,可以購買同行業其餘公司的積分,或者向政府繳納高額罰款。   中國若要實施積分交易制度 政府部門職責需合理分配。   為此,積分政策負責人時間給出幾點建議:作為政策的制定者政府,需要部門間進行合理的職責分配,物質保障方面,中國新能源基礎設施建設尚需進一步完善。意識形態方面,當前,消費者對新能源車的認識還不充足,對制度實施造成一定阻礙。企業方面,不同規模的企業須在政策上區別對待,為中小企業的發展提供空間;當企業規模有所變更時,應當提供相應的扶持政策。   文章來源:人民網

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

  • 國務院:外商可獨資製造新能源汽車動力電池等領域

    國務院:外商可獨資製造新能源汽車動力電池等領域

    7月19日,國務院下發第2016年第41號文件,關於在自由貿易試驗區暫時調整有關行政法規、國務院文件和經國務院批准的部門規章規定的決定。  
      相關自由貿易試驗區涉及上海市、天津市、廣東省、福建省四區域。國務院決定,在自由貿易試驗區暫時調整《中華人民共和國外資企業法實施細則》等18部行政法規、《國務院關於投資體制改革的決定》等4件國務院檔、《外商投資產業指導目錄(2015年修訂)》等4件經國務院批准的部門規章的有關規定。   據分析,此次重大調整專案涉及51項。其中放開合資門檻,允許外商以獨資形式從事生產經營活動的項目多達12項。在新能源汽車關鍵零部件及整車領域,涉及3項,具體如下:   1、允許外商以獨資形式從事能量型動力電池(能量密度≥110Wh,迴圈壽命≥2000次)的製造; 2、允許外商以獨資形式從事汽車電子匯流排網路技術、電動助力轉向系統電子控制器的製造與研發; 3、允許外商以獨資形式從事摩托車生產;   由此可見,在新能源汽車重要零部件動力電池領域,國外一線大廠終於擺脫了合資電池廠的固定模式,三星SDI、松下、LG化學等電池大頭將獲益。   文章來源:上海蓋世

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 蘋果電動車傳技術性問題,將延至2021年亮相

    蘋果(Apple)電動車開發案「泰坦計畫」(Project Titan)又有新傳聞,不過卻是一項令人失望的消息,據悉因技術性問題,故蘋果電動車亮相時間恐延至2021年!

    日本蘋果情報網站gori.me 22日報導,期待在2020年東京奧運駕駛蘋果電動車的夢想恐將破滅,據The Information網站21日指出,蘋果電動車亮相時間已被延後至2021年。

    報導指出,之前曾傳出蘋果電動車最快將在2019年發表、或是將在2020年開始進行生產,不過據The Information指出,蘋果雖持續朝上述2020年的目標進行研發,但因技術性問題,故蘋果電動車亮相時間已延至2021年。蘋果電動車企劃始於2014年,據悉目前參與該企劃的蘋果員工達約1,000人。

    gori.me指出,Google計畫於2020年發表自動駕駛車,因此5年後的IT業界主戰場或許不是智慧手機、也不是穿戴裝置,而是有可能在「車子」。

    根據嘉實XQ全球贏家系統報價,蘋果21日下跌0.53%、收99.43美元,4個交易日來首度走跌。

    9to5Mac、Electrek 4月19日獨家報導,蘋果已聘請特斯拉(Tesla)前任汽車工程副總裁暨英國豪華車商奧斯頓馬丁(Aston Martin)的前任首席工程師Chirs Porritt,而Porritt將負責研究「特殊方案」。大家都知道,所謂的特殊方案就是指蘋果的電動車開發案「泰坦計畫」。

    AppleInsider 4月18日引述法蘭克福廣訊報(Frankfurter Allgemeine Zeitung)報導,蘋果已在柏林設立秘密開發實驗室,目前在當地擁有15-20名工程、軟體、硬體、行銷背景的德國汽車業頂尖人才。報導指出,蘋果進軍汽車業的第一款產品將是電動車、但初期不具備自駕功能。

    不過,MarketWatch 5月26日報導,Edison Investment Research科技分析師Richard Windsor表示,蘋果先前也曾花大錢研發蘋果電視,最後卻從未發布,蘋果車的結局應該也一樣。華爾街日報報導,蘋果曾有意推出55~65吋的蘋果電視,但是因為產品缺乏特色,打消上市念頭,投入心血全數泡湯。

    Windsor強調,蘋果車問題更大,蘋果發現打造汽車比想像更困難,車業門檻極高、管制多、蘋果又缺乏清楚的獲利計畫。儘管蘋果資本雄厚,口袋極深,就算如此,要打入車業也不容易。

    (本文內容由授權提供)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步”網站設計“幫您第一時間規劃公司的門面形象

  • 特斯拉Gigafactory趕工,可望提前量產

    美國電動車商特斯拉(Tesla Motors Inc.)的平價車款「Model 3」預定2018年問世,為了趕上需求,特斯拉造價50億美元的「Gigafactory」超級電池廠正在加緊趕工,將比原定時程提前數年、明(2017)年初就可以開始量產車用鋰電池。

    華爾街日報、Electrek 24日報導,坐落於內華達州雷諾市的Gigafactory目前占地超過3,000英畝,特斯拉已加倍聘請建築工人,共計1,000名人員一週兩班每天輪流趕工,預計明年初就可大功告成。特斯拉技術長兼共同創辦人JB Straubel表示,在汽車量產前,電池和電池組的組裝廠房一定要事先完工,因此無論是建廠計畫或是電池的擴產時間表都會加快進度。

    特斯拉電池供應夥伴Panasonic Corp.已承諾要為該廠提供16億美元的資金,目前則因為找不到合適的人才而傷透腦筋。特斯拉執行長Elon Musk預估,Gigafactory完工之後,2020年將可年產105GW的電池,足以供應120萬台Model S豪華電動轎車所需,但其中有1/3將用於定置型電池儲存裝置(stationary battery storage product)。

    假如這座廠房能如期完工、擴產,則其產能將是全球現有電池廠的10倍之多,這也使得北美的鋰礦開採活動大增。

    OilPrice.com 21日報導,電池過去幾年來的需求倍數成長,鋰已成為今(2016)年來最夯的金屬、擊敗黃金,雖然最近幾個月的價格漲勢稍緩,但強勁的基本面顯示其長期前景依舊看俏。另外,在戴姆勒(Daimler)、日產汽車等業者的推波助瀾下,預估到了2016年插電式電動車銷售量(plug-in electric vehicle,簡稱PEV)有望年增62%,2017年、2018年更有望成長60%、100%。這相當於2018年會賣出60萬輛PEV。

    (本文內容由授權提供)

     

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • mysql 索引筆記

    mysql 索引筆記

    MyISAM引擎的B+Tree的索引

    通過上圖可以直接的看出, 在MyISAM對B+樹的運用中明顯的特點如下:

    • 所有的非恭弘=叶 恭弘子節點中存儲的全部是索引信息
    • 在恭弘=叶 恭弘子節點中存儲的 value值其實是 數據庫中某行數據的index

    MyISAM引擎 索引文件的查看:

    在 /var/lib/mysql目錄中

    .myd 即 my data , 數據庫中表的數據文件

    .myi 即 my index , 數據庫中 索引文件

    .log 即 mysql的日誌文件

    InnoDB引擎 索引文件的查看:

    同樣在 /var/lib/mysql 目錄下面

    InnoDB引擎的B+Tree的索引

    InnoDB的實現方式業內也稱其為聚簇索引, 什麼是聚簇索引呢? 就是相鄰的行的簡直被存儲到一起, 對比上面的兩幅圖片就會發現, 在InnDB中, B+樹的恭弘=叶 恭弘子節點中存儲的是數據行中的一行行記錄, 缺點: 因為索引文件被存放在硬盤上, 所以很占硬盤的空間

    一般我們會在每一個表中添加一列 取名 id, 設置它為primary key , 即將他設置成主鍵, 如果使用的存儲引擎也是InnoDB的話, 底層就會建立起主鍵索引, 也是聚簇索引, 並且會自動按照id的大小為我們排好序,(因為它的一個有序的樹)

    局部性原理

    局部性原理是指CPU訪問存儲器時,無論是存取指令還是存取數據,所訪問的存儲單元都趨於聚集在一個較小的連續區域中。 更進一步說, 當我們通過程序向操作系統發送指令讓它讀取我們指定的數據時, 操作系統會一次性讀取一頁(centos 每頁4kb大小,InnoDB存儲引擎中每一頁16kb)的數據, 它遵循局部性理論, 猜測當用戶需要使用某個數據時, 用戶很可能會使用這個數據周圍的數據,故而進行一次

    InnoDB的頁格式

    什麼是頁呢? 簡單說,就是一條條數據被的存儲在磁盤上, 使用數據時需要先將數據從磁盤上讀取到內存中, InnoDB每次讀出數據時同樣會遵循 局部性原理, 而不是一條條讀取, 於是InnoDB將數據劃分成一個一個的頁, 以頁作為和磁盤之間交互的基本單位

    通過如下sql, 可以看到,InnoDB中每一頁的大小是16kb

    show global status like 'Innodb_page_size';

    名稱 簡述
    File Header 文件頭部, 存儲頁的一些通用信息
    Page Header 頁面頭部, 存儲數據頁專有的信息
    Infinum + supremum 最大記錄和最小記錄, 這是兩個虛擬的行記錄
    User Records 用戶記錄, 用來實際存儲行記錄中的內容
    Free Space 空閑空間, 頁中尚位使用的空間
    Page Directory 頁面目錄, 存儲頁中某些記錄的位置
    File Tailer 文件尾部 , 用來校驗頁是否完整

    InnoDB的行格式 compact

    每一頁中存儲的行數據越多. 整體的性能就會越強

    compact的行格式如下圖所示

    可以看到在行格式中在存儲真正的數據的前面會存儲一些其他信息, 這些信息是為了描述這條記錄而不得不添加的一些信息, 這些額外的信息就是上圖中的前三行

    • 變長字段的長度列表

    在mysql中char是固定長度的類型, 同時mysql還支持諸如像 varchar這樣可變長度的類型, 不止varchar , 想 varbinary text blob這樣的變長數據類型, 因為 變長的數據類型的列存儲的數據的長度是不固定的, 所以說我們在存儲真正的數據時, 也得將這些數據到底佔用了多大的長度也給保存起來

    • NULL標誌位

    compact行格式會將值可以為NULL的列統一標記在 NULL標誌位中, 如果數據表中所有的字段都被標記上not null , 那麼就沒有NULL值列表

    • 記錄頭信息

    記錄頭信息, 顧名思義就是用來描述記錄頭中的信息, 記錄頭信息由固定的5個字節組成, 一共40位, 不同位代表的意思也不同, 如下錶

    名稱 單位 bit 簡介
    預留位1 1 未使用
    預留位2 1 未使用
    delete_mark 1 標記改行記錄是否被刪除了
    min_rec_mark 1 標記在 B+樹中每層的非恭弘=叶 恭弘子節點中最小的node
    n_owned 4 表示當前記錄擁有的記錄數
    heap_no 13 表示當前記錄在堆中的位置
    record_type 3 表示當前記錄的類型 , 0表示普通記錄, 1表示B+樹中非恭弘=叶 恭弘子節點記錄, 2表示最小記錄 ,3表示最大記錄
    next_record 16 表示下一條記錄的相對位置

    行溢出

    在mysql中每一行, 能存儲的最大的字節數是65535個字節數, 此時我們使用下面的sql執行時就會出現行溢出現象

    CREATE TABLE test ( c VARCHAR(65535) ) CHARSET=ascii ROW_FORMAT=Compact;

    給varchar申請最大65535 , 再加上compact行格式中還有前面三個非數據列佔用內存,所以一準溢出, 如果不想溢出, 可以適當的將 65535 – 3

    頁溢出

    前面說了, InnoDB中數據的讀取按照頁為單位, 每一頁的大小是 16kb, 換算成字節就是16384個字節, 但是每行最多存儲 65535個字節啊, 也就是說一行數據可能需要好幾個頁來存儲

    怎麼辦呢?

    • compact行格式會在存儲真實數據的列中多存儲一部分數據, 這部分數據中存儲的就是下一頁的地址
    • dynamic行格式 中直接存儲數據所在的地址, 換句話說就是數據都被存儲在了其他頁上
    • compressed行格式會使用壓縮算法對行格式進行壓縮處理

    一般我們都是將表中的id列設置為主鍵, 這就會形成主鍵索引, 於是我們需要注意了:

    主鍵的佔用的空間越小,整體的檢索效率就會越高

    為什麼這麼說呢? 這就可以結合頁的概念來解析, 在B+樹這種數據結果中, 恭弘=叶 恭弘子節點中用來存儲數據, 存儲數據的格式類似Key-value key就是索引值, value就是數據內容, 如果索引佔用的空間太大的話, 單頁16kb能存儲的索引就越小, 這就導致數據被分散在更多的頁上, 致使查詢的效率降低

    建立索引的技巧

    為某一列建立索引

    給text表中的title列創建索引, 索引名字 my_index
    alter table text add index my_index (title);

    雖然建立索引能提升查詢的效率, 根據前人的經驗看, 這並不是一定的, 建立索引本身會直接消耗內存空間, 同時索, 插入,刪除, 這種寫操作就會打破B+樹的平衡面臨索引的重建, 一般出現如下兩種情況時,是不推薦建立索引的

    1. 表中的數據本身就很少
    2. 我們計算一下索引的選擇性很低

    兼顧 – 索引的選擇性與前綴索引

    所謂選擇性,其實就是說不重複出現的索引值(基數,Cardinality) 與 表中的記錄數的比值

    即: 選擇性= 基數 / 記錄數

    選擇性的取值範圍在(0,1]之間, 選擇性越接近1 , 說明建立索引的必要性就越強, 比如對sex列進行建立索引,這裏面非男即女, 如果對它建立索引的話, 其實是沒意義的, 還不如直接進行全表掃描來的快

    如何使用sql計算選擇性呢? 嚴格遵循上面的公式

    SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles;
    count(基數/記錄數)
    DISTINCT(title) / /count(*)

    更詳細的例子看下面的連接

    索引失效問題

    注意事項

    • 索引無法存儲null值

    • 如果條件中有or, 即使條件中存在索引也不會使用索引,如果既想使用or,又想使用索引, 就給所有or條件控制的列加上索引

    • 使用like查詢時, 如果以%開頭,肯定是進行全表掃描

    • 使用like查詢時, 如果%在條件後面

      • 對於主鍵索引, 索引失效
      • 對於普通索引, 索引不失效
    • 如果列的類型是字符串類型, 那麼一定要在條件中將數據用引號引起來,不然也會是索引失效

    • 如果mysql認為全表掃描比用索引塊, 同樣不會使用索引

    聯合索引

    什麼是聯合索引

    聯合索引, 也叫複合索引,說白了就是多個字段一起組合成一個索引

    像下面這樣使用 id + title 組合在一起構成一個聯合索引

    CREATE TABLE `text` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `title` varchar(255) NOT NULL,
      `content` text NOT NULL,
      PRIMARY KEY (`id`,`title`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3691 DEFAULT CHARSET=utf8
    • 如果我們像上圖那樣創建了索引,我們只要保證我們的 id+title 兩者結合起來全局唯一就ok
    • 建立聯合索引同樣是需要進行排序的,排序的規則就是按照聯合索引所有列組成的字符串的之間的先後順序進行排序, 如a比b優先

    左前原則

    使用聯合索引進行查詢時一定要遵循左前綴原則, 什麼是左前綴原則呢? 就是說想讓索引生效的話,一定要添加上第一個索引, 只使用第二個索引進行查詢的話會導致索引失效

    比如上面創建的聯合索引, 假如我們的查詢條件是 where id = ‘1’ 或者 where id = ‘1’ and title = ‘唐詩宋詞’ 索引都會不失效

    但是如果我們不使用第一個索引id, 像這樣 where title = ‘唐詩’ , 結果就是導致索引失效

    聯合索引的分組&排序

    還是使用這個例子:

    CREATE TABLE `text` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `title` varchar(255) NOT NULL,
      `content` text NOT NULL,
      PRIMARY KEY (`id`,`title`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3691 DEFAULT CHARSET=utf8

    demo1: 當我們像下面這樣寫sql時, 就會先按照id進行排序, 當id相同時,再按照title進行排序

    select * form text order by id, title;

    demo2: 當我們像下面這樣寫sql時, 就會先將id相同的劃分為一組, 再將title相同的劃分為一組

    select id,title form text group by id, title;

    demo3: ASC和DESC混用, 其實大家都知道底層使用B+樹, 本身就是有序的, 要是不加限制的話,默認就是ASC, 反而是混着使用就使得索引失效

    select * form text order by id ASC, title DESC;

    如何定位慢查詢

    相關參數

    名稱 簡介
    slow_query_log 慢查詢的開啟狀態
    slow_query_log_file 慢查詢日誌存儲的位置
    long_query_time 查詢超過多少秒才記錄下來

    常用sql

    # 查看mysql是否開啟了慢查詢
    show variables like 'slow_query_log';   
    # 將全局變量設置為ON
    set global slow_query_log ='on';
    # 查看慢查詢日誌存儲的位置
    show variables like 'slow_query_log_file';
    # 查看規定的超過多少秒才被算作慢查詢記錄下來
    show variables like 'long_query_time';
    show variables like 'long_query%';
    # 超過一秒就記錄 , 每次修改這個配置都重新建立一次鏈接
    set global long_query_time=1; 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

  • 基於.NetStandard的簡易EventBus實現-基礎實現

    基於.NetStandard的簡易EventBus實現-基礎實現

    一、問題背景

      最近離職來到了一家新的公司,原先是在乙方工作,這回到了甲方,在這一個月中,發現目前的業務很大一部分是靠輪詢實現的,例如:通過輪詢判斷數據處於B狀態了,則輪詢到數據后執行某種動作,這個其實是非常浪費的,並且對於數據的實時性也會不怎麼友好,基於以上的情況,在某天開車堵車時候,想到了之前偶然了解過的事件總線(EventBus),對比了公司當前的場景后,覺得事件總線應該是可以滿足需求的(PS:只是我覺得這個有問題,很多人不覺得有問題),那既然想到了,那就想自己是否可以做個事件總線的輪子

    二、什麼是事件總線

      我們知道事件是由一個Publisher跟一個或多個的Subsriber組成,但是在實際的使用過程中,我們會發現,Subsriber必須知道Publisher是誰才可以註冊事件,進而達到目的,那這其實就是一種耦合,為了解決這個問題,就出現了事件總線的模式,事件總線允許不同的模塊之間進行彼此通信而又不需要相互依賴,如下圖所示,通過EventBus,讓Publisher以及Subsriber都只需要對事件源(EventData)進行關注,不用管Publisher是誰,那麼EventBus主要是做了一些什麼事呢?

    三、EventBus做了什麼事?

      1、EventBus實現了對於事件的註冊以及取消註冊的管理

      2、EventBus內部維護了一份事件源與事件處理程序的對應關係,並且通過這個對應關係在事件發布的時候可以找到對應的處理程序去執行

      3、EventBus應該要支持默認就註冊事件源與處理程序的關係,而不需要開發人員手動去註冊(這裏可以讓開發人員去控制自動還是手動)

    四、具體實現思路

       首先在事件總線中,存在註冊、取消註冊以及觸發事件這三種行為,所以我們可以將這三種行為抽象一個接口出來,最終的接口代碼如下:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MEventBus.Core
    {
        public interface IEventBus
        {
            #region 接口註冊
            void Register<TEventData>(Type handlerType) where TEventData : IEventData;
            void Register(Type eventType, Type handlerType);
            void Register(string eventType, Type handlerType);
            #endregion
    
            #region 接口取消註冊
            void Unregister<TEventData>(Type handler) where TEventData : IEventData;
            void Unregister(Type eventType, Type handlerType);
            void Unregister(string eventType, Type handlerType);
            #endregion
    
    
            void Trigger(string pubKey, IEventData eventData);
            Task TriggerAsync(string pubKey, IEventData eventData);
            Task TriggerAsync<TEventData>(TEventData eventData) where TEventData : IEventData;
            void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData;
        }
    }
    

      在以上代碼中發現有些方法是有IEventData約束的,這邊IEventData就是約束入參行為,原則上規定,每次觸發的EventData都需要繼承IEventData,而註冊的行為也是直接跟入參類型相關,具體代碼如下:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace MEventBus.Core
    {
        public interface IEventData
        {
            string Id { get; set; }
            DateTime EventTime { get; set; }
            object EventSource { get; set; }
        }
    }
    

      接下來我們看下具體的實現代碼

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MEventBus.Core
    {
        public class EventBus : IEventBus
        {
            private static ConcurrentDictionary<string, List<Type>> dicEvent = new ConcurrentDictionary<string, List<Type>>();
            private IResolve _iresolve { get; set; }
            public EventBus(IResolve resolve)
            {
                _iresolve = resolve;
                InitRegister();
            }
    
            public void InitRegister()
            {
                if (dicEvent.Count > 0)
                {
                    return;
                }
                //_iresolve = ioc_container;
                dicEvent = new ConcurrentDictionary<string, List<Type>>();
                //自動掃描類型並且註冊
                foreach (var file in Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll"))
                {
                    var ass = Assembly.LoadFrom(file);
                    foreach (var item in ass.GetTypes().Where(p => p.GetInterfaces().Contains(typeof(IEventHandler))))
                    {
                        if (item.IsClass)
                        {
                            foreach (var item1 in item.GetInterfaces())
                            {
                                foreach (var item2 in item1.GetGenericArguments())
                                {
                                    if (item2.GetInterfaces().Contains(typeof(IEventData)))
                                    {
                                        Register(item2, item);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            //註冊以及取消註冊的時候需要加鎖處理
            private static readonly object obj = new object();
    
            #region 註冊事件
            public void Register<TEventData>(Type handlerType) where TEventData : IEventData
            {
                //將數據存儲到mapDic
                var dataType = typeof(TEventData).FullName;
                Register(dataType, handlerType);
            }
            public void Register(Type eventType, Type handlerType)
            {
                var dataType = eventType.FullName;
                Register(dataType, handlerType);
            }
            public void Register(string pubKey, Type handlerType)
            {
                lock (obj)
                {
                    //將數據存儲到dicEvent
                    if (dicEvent.Keys.Contains(pubKey) == false)
                    {
                        dicEvent[pubKey] = new List<Type>();
                    }
                    if (dicEvent[pubKey].Exists(p => p.GetType() == handlerType) == false)
                    {
                        //IEventHandler obj = Activator.CreateInstance(handlerType) as IEventHandler;
                        dicEvent[pubKey].Add(handlerType);
                    }
                }
            }
    
    
    
            #endregion
    
            #region 取消事件註冊
            public void Unregister<TEventData>(Type handler) where TEventData : IEventData
            {
                var dataType = typeof(TEventData);
                Unregister(dataType, handler);
            }
    
            public void Unregister(Type eventType, Type handlerType)
            {
                string _key = eventType.FullName;
                Unregister(_key, handlerType);
            }
            public void Unregister(string eventType, Type handlerType)
            {
                lock (obj)
                {
                    if (dicEvent.Keys.Contains(eventType))
                    {
                        if (dicEvent[eventType].Exists(p => p.GetType() == handlerType))
                        {
                            dicEvent[eventType].Remove(dicEvent[eventType].Find(p => p.GetType() == handlerType));
                        }
                    }
                }
            }
            #endregion
    
            #region Trigger觸發
            //trigger時候需要記錄到數據庫
            public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData
            {
                var dataType = eventData.GetType().FullName;
                //獲取當前的EventData綁定的所有Handler
                Notify(dataType, eventData);
            }
    
            public void Trigger(string pubKey, IEventData eventData)
            {
                //獲取當前的EventData綁定的所有Handler
                Notify(pubKey, eventData);
            }
            public async Task TriggerAsync<TEventData>(TEventData eventData) where TEventData : IEventData
            {
                await Task.Factory.StartNew(new Action(()=> 
                {
                    var dataType = eventData.GetType().FullName;
                    Notify(dataType, eventData);
                }));
            }
            public async Task TriggerAsync(string pubKey, IEventData eventData)
            {
                await Task.Factory.StartNew(new Action(() =>
                {
                    var dataType = eventData.GetType().FullName;
                    Notify(pubKey, eventData);
                }));
            }
            //通知每成功執行一個就需要記錄到數據庫
            private void Notify<TEventData>(string eventType, TEventData eventData) where TEventData : IEventData
            {
                //獲取當前的EventData綁定的所有Handler
                var handlerTypes = dicEvent[eventType];
                foreach (var handlerType in handlerTypes)
                {
                    var resolveObj = _iresolve.Resolve(handlerType);
                    IEventHandler<TEventData> handler = resolveObj as IEventHandler<TEventData>;
                    handler.Handle(eventData);
    
                }
            }
            #endregion
        }
    }
    

      代碼說明:

      1、如上的EventBus是繼承了IEventBus后的具體實現,小夥伴可能看到在構造函數里,有一個接口參數IResolve,這個主要是為了將解析的過程進行解耦,由於在一些WebApi的項目中,更加多的是使用IOC的機制進行對象的創建,那基於IResolve就可以實現不同的對象創建方式(內置的是通過反射實現)

      2、InitRegister方法通過遍歷當前目錄下的dll文件,去尋找所有實現了IEventHandler<IEventData>接口的信息,並且自動註冊到EventBus中,所以在實際使用過程中,應該是沒有機會去適用register註冊的

      3、觸發機制實現了同步以及異步的調用,這個從方法命名中就可以看出來

    五、程序Demo

      TestHandler2(繼承IEventHandler)

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;
    using MEventBus.Core;
    
    namespace MEventBusHandler.Test
    {
        public class TestHandler2 : IEventHandler<TestEventData>
        {
            public void Handle(TestEventData eventData)
            {
                Thread.Sleep(2000);
                MessageBox.Show(eventData.EventTime.ToString());
            }
        }
    }
    

      TestEventData(繼承EventData,EventData是繼承了IEventData的代碼)

    using MEventBus.Core;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace MEventBusHandler.Test
    {
        public class TestEventData : EventData
        { }
    }
    

      調用代碼

    using MEventBus.Core;
    using MEventBusHandler.Test;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace MEventBus.Test
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                TestHandler.OnOut += TestHandler_OnOut;
            }
    
            private void TestHandler_OnOut(object sender, EventArgs e)
            {
                MessageBox.Show("Hello World");
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                var task = new MEventBus.Core.EventBus(new ReflectResolve()).TriggerAsync(new TestEventData());
                task.ContinueWith((obj) => {
                    MessageBox.Show("事情全部做完");
                });
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
               new EventBus(new ReflectResolve()).Trigger(new TestEventData());
            }
        }
    
    
    }
    

      執行結果

     

     

     

     

     我在真正的Demo中,其實是註冊了2個handler,可以在後續公布的項目地址里看到

    六、總結

      從有這個想法開始,到最終實現這個事件總線,大概總共花了2,3天的時間(PS:晚上回家獨自默默幹活),目前只能說是有一個初步可以使用的版本,並且還存在着一些問題:

      1、在.NetFrameWork下(目前公司還不想升級到.NetCore,吐血。。),如果使用AutoFac創建EventBus(單例模式下),如果Handler也使用AutoFac進行創建,會出現要麼對象創建失敗,要麼handler里的對象與調用方的對象不是同一個實例,為了解決這個問題,我讓EventBus不再是單例模式,將dicEvent變成了靜態,暫時表面解決

      2、未考慮跨進程的實現(感覺用savorboard大佬的就可以了)

      3、目前這個東西在一個小的新項目里使用,暫時在測試環境還算沒啥問題,各位小夥伴如果有類似需求,可以做個參考

      由於個人原因,在測試上可能會有所不夠,如果有什麼bug的話,還請站內信告知,感謝(ps:文字表達弱雞,技術渣渣,各位多多包涵)

      最後:附上項目地址:

     

     

    作者: Mango

    出處: 

    關於自己:專註.Net桌面開發以及Web後台開發,對.NetCore、微服務、DevOps,K8S等感興趣,最近到了個甲方公司準備休養一段時間

    本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,如有問題, 可站內信告知.

     

     

     

     

     

     

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • CSS(8)—通俗講解定位(position)

    CSS(8)—通俗講解定位(position)

    CSS(8)—通俗講解定位(position)

    CSS有三種基本的定位機制: 普通流浮動定位。前面兩個之前已經講過,詳見博客:

    1、

    2、

    3、

    一、為什麼要用定位?

    如果說浮動關鍵在一個 “浮” 字上面, 那麼 我們的定位,關鍵在於一個 “位” 上。

    我們來思考下定位用於的場景。

    1、打Log標籤

    比如你想在商品的圖片想打個標籤比如:包郵、最新上架等等。

    怎麼做比較好呢,如果你要粗暴那就直接ps在圖片上添加標籤。只是這樣有個很大的弊端,比如你要添加新標籤你需要重現修圖,比如商品之前包郵後面不包郵了,

    那你又需要重新p圖。這樣肯定是不合適的。那怎麼做比較合適?

    其實很簡單,將商品圖片和標籤的標籤分開來。然後通過css在商品圖片上添加標籤。這個時候通常會定位去完成。

    2、切換Banner

    有些商城的首頁都會有個Banner,這裏 左右的箭頭下面的小點點一般也是用定位來做。

    3、廣告位窗口

    有些位置在左右側會有固定的廣告窗口,不論怎麼滑動頁面這個廣告窗口都是在固定位置

    這個就需要用到固定定位了。

    二、定位概念

    1、定位的分類

    在CSS中,position 屬性用於定義元素的定位模式,其基本語法格式如下:

    選擇器 {position:屬性值;}

    屬性值

    這裏還有個概念就是 邊偏移 因為你定位肯定要指定定位在哪裡,所以需要通過 邊偏移 來指定。

    所以定位是要和邊偏移搭配使用的。不過對於static(靜態定位)設置邊偏移是無用的。

    2、靜態定位(static)

    static 是此屬性的默認值。這時候,我們稱那個元素沒有被定位。簡單來說就是網頁中所有元素都默認的是靜態定位。 其實就是標準流的特性。

    所以如果需要使用定位那這裏就不能是這個默認值了。

    注意 在靜態定位狀態下,此時 top, right, bottom, left 和 z-index 屬性無效。

    3、相對定位(relative)

    它的主要特點如下

    1、 參照元素原來的位置進行移動。
    2、 通過"left"、 "top"、 "right"、 "bottom" 屬性進行定位。
    3、 元素原有的空間位保留。
    4、 元素在移動時會蓋住其他元素。

    舉例說明

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>相對定位</title>
            <style type="text/css">
                #one {
                    width: 120px;
                    height: 120px;
                    background: #E19D59;
                }
                #two {
                    width: 120px;
                    height: 120px;
                    background: #FF0000;
                    position: relative;   /*設置相對定位*/
                    left: 20px;           /*設置距離左邊偏移位置*/
                    top: 20px;            /*設置距離頂部偏移位置*/
                }
                #three {
                    width: 120px;
                    height: 120px;
                    background: #008000;
                }
            </style>
        </head>
        <body>      
            <div id="one">div1</div>
            <div id="two">div2</div>
            <div id="three">div3</div>        
        </body>
    </html>

    運行結果

    通過我們這個示例我們可以看出

    1、它的左右,上下邊偏移的量是根據這個div2原始位置基礎上進行移動的。
    2、這個div2它還是個標準流,並沒有浮起來,所以這個位置它還是佔有的。(如果div2浮動那麼div3就會向上移動,這裏顯然沒有)
    3、當它偏移后 如果和其它元素有重疊,它會覆蓋其它元素。(div2覆蓋了部分div3元素)

    作用 我的理解相對定位主要用途是用來給絕對定位的一個盒子。(下面會解釋這句話)

    4、絕對定位absolute

    特點

    1、參照距離他最近的有定位屬性的父級元素進行移動
    2、通過"left"、 "top"、 "right"、 "bottom" 屬性進行定位
    3、元素完全脫離文檔流,原有位置不再保留
    4、元素在移動時會蓋住其他元素
    5、一般我們設置絕對定位時,都會找一個合適的父級將其設置為相對定位。最好為這個具有相對定位屬性的父級設置寬高

    舉例說明

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
            <style type="text/css">
               #father{
                    width: 400px;
                    height: 400px;
                    margin: 100px;
                   /*  position: relative;*/
                    background: yellow;
                }
                #bd1{
                    width: 120px;
                    height: 120px;
                    background: #E19D59;
                }
                #bd2{
                    width: 120px;
                    height: 120px;
                    background: #FF0000;
                    position: absolute;
                    left: 20px;
                    top: 20px;
                }
                #bd3{
                    width: 120px;
                    height: 120px;
                    background: #008000;
                }
            </style>
        </head>
        <body>  
        <div id="father"> 
            <div id="bd1">div1</div>
            <div id="bd2">div2</div>
            <div id="bd3">div3</div>  
        </div>         
        </body>
    </html>

    運行結果

    從這幅圖可以看出一點

    這裏因為父div沒有設置定位,所以它的位置是相對於body進行邊偏移。

    這個時候我們將父標籤設置 position: relative;

    再刷新頁面

    從這張圖很直觀看到:

    1、因為父div設置了定位,所以這裏的邊偏移變成都是相對於父div進行偏移(正常貼標籤就是這樣)
    2、我們可以看出當設置絕對定位后,該元素已經脫離文檔流,已經浮上來了(因為div2上浮所有div3才會上移)
    3、元素在移動時會蓋住其他元素 (div2覆蓋了部分div3)

    5、固定定位(fixed)

    特點

    1、以body為定位時的對象,總是根據瀏覽器的窗口來進行元素的定位
    2、通過"left"、 "top"、 "right"、 "bottom" 屬性進行定位
    3、元素完全脫離文檔流,原有位置不再保留
    4、元素不會隨着文檔流的滑動而滑動

    固定定位最大的特點就是第一點,可以理解成它是以可視區域為準,會一直显示在可視區域,屏幕滑動也會显示在定位的位置。

    6、四種定位總結

    還有比較重要的三點

    定位模式轉換

    跟 浮動一樣, 元素添加了 絕對定位和固定定位之後, 元素模式也會發生轉換, 都自動轉換為 行內塊元素。

    絕對定位的盒子水平/垂直居中

    注意 普通的盒子是左右margin 改為 auto就可, 但是對於絕對定位就無效了。

    定位的盒子也可以水平或者垂直居中,有一個算法(下面會舉例說明)。

    1. 首先left 50%   父盒子的一半大小
    2. 然後走自己外邊距負的一半值就可以了 margin-left。

    子絕父相

    這句話的意思是 子級是絕對定位的話,父級要用相對定位

    為什麼會有這個概念,那是因為絕對定位的邊偏移特點是

     如果父元素沒有設置定位,那麼它的位置是相對於body進行邊偏移。如果父元素設置定位,那就根據父元素偏移。

    一般我們肯定是希望根據父元素偏移。就好比圖片打標籤,不可能跟着body偏移而是父元素進行定位。而且父元素相對定位最大的好處就是它會佔有位置,因此父親最好是 相對定位。

    三、經典示例

    1、打上log標記

    示例

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>log標籤</title>
        <style>
        div {
            width: 310px;
            height: 190px;
            border: 1px solid #ccc;
            margin: 100px auto; 
            position: relative;  /*父選擇相對定位*/
        }
        .top {
            position: absolute; /*子取相對定位*/
            top: 0;             /*位置 左上*/
            left: 0;
        }
        
        </style>
    </head>
    <body>
        <div>
            <img src="images/log.jpg" alt="" class="top">     <!-- log的圖片 -->
            <img src="images/goods.jpg" height="190" width="310" alt=""> <!-- 商品圖片,長和寬和父div大小一致 -->
        </div>
    </body>
    </html>

    運行結果就是上面的最終結果。

    2、定位水平居中

    加了定位 浮動的的盒子 margin 0 auto 失效了

    代碼

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>水平居中</title>
        <style>
        div {
            width: 200px;
            height: 200px;
            background-color: pink;
            position: absolute;
            /*加了定位 浮動的的盒子  margin 0 auto 失效了*/
            left: 50%;
            margin-left: -100px;  /*減去總寬度一般*/
            top: 50%;
            margin-top: -100px;   /*減去總高度一般*/
        }
        </style>
    </head>
    <body>
        <div></div>
    </body>
    </html>

    這個這個div就處於整個頁面的居中了,這裏我們來說明下下面這兩個的意思

            left: 50%;
            margin-left: -100px;  /*減去總寬度一般*/

    3、輪播圖

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>輪播圖</title>
        <style>
        * {
            margin: 0;
            padding: 0;
        }
        li {
            list-style: none;
        }
        .tb {
            width: 520px;
            height: 280px;
            background-color: pink;
            margin: 100px auto;
            position: relative;
        }
        .tb a {
            width: 24px;
            height: 36px;
            
            display: block;
            position: absolute;
            top: 50%;
            margin-top: -18px;
        }
        .left {
            left: 0;
            background: url(images/left.png) no-repeat;
        }
        .right {
            right: 0;
            background: url(images/right.png) no-repeat;
        }
        .tb ul {
            width: 70px;
            height: 13px;
            background: rgba(255, 255, 255, .3);
            position: absolute; /* 加定位*/
            bottom: 18px;
            left: 50%; /*水平走父容器的一半*/
            margin-left: -35px; /*左走自己的一半*/
            border-radius: 8px;
        }
        .tb ul li {
            width: 8px;
            height: 8px;
            background-color: #fff;
            float: left;
            margin: 3px;
            border-radius: 50%;
        }
        .tb .current {
            background-color: #f40;
        }
        </style>
    </head>
    <body>
        <div class="tb">
            <img src="images/tb.jpg" >
            <a href="#" class="left"></a>
            <a href="#" class="right"></a>
            <ul>
                <li class="current"></li>
                <li></li>
                <li></li>
                <li></li>
                <li></li>
            </ul>
    
        </div>
    </body>
    </html>

    運行結果

    參考

    1、

    2、

    你如果願意有所作為,就必須有始有終。(10)

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

  • 星際爭霸2 AI開發(持續更新)

    星際爭霸2 AI開發(持續更新)

    準備

    我的環境是python3.6,sc2包0.11.1
    機器學習包下載鏈接:
    地圖下載鏈接
    pysc2是DeepMind開發的星際爭霸Ⅱ學習環境。 它是封裝星際爭霸Ⅱ機器學習API,同時也提供Python增強學習環境。
    以神族為例編寫代碼,神族建築科技圖如下:

    採礦

    # -*- encoding: utf-8 -*-
    '''
    @File    :   __init__.py.py    
    @Modify Time      @Author       @Desciption
    ------------      -------       -----------
    2019/11/3 12:32   Jonas           None
    '''
    
    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    
    
    class SentdeBot(sc2.BotAI):
        async def on_step(self, iteration: int):
            await self.distribute_workers()
    
    
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy)
    ],realtime = True)

    注意
    game_data.py的assert self.id != 0註釋掉
    pixel_map.py的assert self.bits_per_pixel % 8 == 0, "Unsupported pixel density"註釋掉
    否則會報錯

    運行結果如下,农民開始採礦

    可以正常採礦

    建造农民和水晶塔

    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    from sc2.constants import *
    
    
    class SentdeBot(sc2.BotAI):
        async def on_step(self, iteration: int):
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
            for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                # 是否有50晶體礦
                if self.can_afford(UnitTypeId.PROBE):
                    await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且水晶不是正在建造
            if self.supply_left<5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON,near=nexuses.first)
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy)
    ],realtime = True)
    

    運行結果如下,基地造农民,农民造水晶

    收集氣體和開礦

    代碼如下

    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    from sc2.constants import *
    
    
    class SentdeBot(sc2.BotAI):
        async def on_step(self, iteration: int):
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
            for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                # 是否有50晶體礦
                if self.can_afford(UnitTypeId.PROBE):
                    await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene))
    
        ## 開礦
        async def expand(self):
            if self.units(UnitTypeId.NEXUS).amount<3 and self.can_afford(UnitTypeId.NEXUS):
                await self.expand_now()
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy)
    ], realtime=False)
    

    run_game的realtime設置成False,可以在加速模式下運行遊戲。
    運行效果如下:

    可以建造吸收廠和開礦

    建造軍隊

    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    from sc2.constants import *
    
    
    class SentdeBot(sc2.BotAI):
        async def on_step(self, iteration: int):
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
            await self.offensive_force_buildings()
            await self.build_offensive_force()
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
            for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                # 是否有50晶體礦
                if self.can_afford(UnitTypeId.PROBE):
                    await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene))
    
        ## 開礦
        async def expand(self):
            if self.units(UnitTypeId.NEXUS).amount<2 and self.can_afford(UnitTypeId.NEXUS):
                await self.expand_now()
    
        ## 建造進攻性建築
        async def offensive_force_buildings(self):
            if self.units(UnitTypeId.PYLON).ready.exists:
                pylon = self.units(UnitTypeId.PYLON).ready.random
                if self.units(UnitTypeId.PYLON).ready.exists:
                    # 根據神族建築科技圖,折躍門建造過後才可以建造控制核心
                    if self.units(UnitTypeId.GATEWAY).ready.exists:
                        if not self.units(UnitTypeId.CYBERNETICSCORE):
                            if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
                                await self.build(UnitTypeId.CYBERNETICSCORE,near = pylon)
                    # 否則建造折躍門
                    else:
                        if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
                            await self.build(UnitTypeId.GATEWAY,near=pylon)
    
        # 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue:
                if self.can_afford(UnitTypeId.STALKER) and self.supply_left>0:
                    await self.do(gw.train(UnitTypeId.STALKER))
    
    
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy)
    ], realtime=False)
    

    運行結果如下:

    可以看到,我們建造了折躍門和控制核心並訓練了追獵者

    控制部隊進攻

    代碼如下

    
    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    from sc2.constants import *
    import random
    
    class SentdeBot(sc2.BotAI):
        async def on_step(self, iteration: int):
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
            await self.offensive_force_buildings()
            await self.build_offensive_force()
            await self.attack()
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
            for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                # 是否有50晶體礦
                if self.can_afford(UnitTypeId.PROBE):
                    await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene))
    
        ## 開礦
        async def expand(self):
            if self.units(UnitTypeId.NEXUS).amount<3 and self.can_afford(UnitTypeId.NEXUS):
                await self.expand_now()
    
        ## 建造進攻性建築
        async def offensive_force_buildings(self):
            if self.units(UnitTypeId.PYLON).ready.exists:
                pylon = self.units(UnitTypeId.PYLON).ready.random
                # 根據神族建築科技圖,折躍門建造過後才可以建造控制核心
                if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE):
                    if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
                        await self.build(UnitTypeId.CYBERNETICSCORE,near = pylon)
                # 否則建造折躍門
                elif len(self.units(UnitTypeId.GATEWAY))<=3:
                    if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
                        await self.build(UnitTypeId.GATEWAY,near=pylon)
    
        ## 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue:
                if self.can_afford(UnitTypeId.STALKER) and self.supply_left>0:
                    await self.do(gw.train(UnitTypeId.STALKER))
    
        ## 尋找目標
        def find_target(self,state):
            if len(self.known_enemy_units)>0:
                # 隨機選取敵方單位
                return random.choice(self.known_enemy_units)
            elif len(self.known_enemy_units)>0:
                # 隨機選取敵方建築
                return random.choice(self.known_enemy_structures)
            else:
                # 返回敵方出生點位
                return self.enemy_start_locations[0]
    
        ## 進攻
        async def attack(self):
            # 追獵者數量超過15個開始進攻
            if self.units(UnitTypeId.STALKER).amount>15:
                for s in self.units(UnitTypeId.STALKER).idle:
                    await self.do(s.attack(self.find_target(self.state)))
    
            # 防衛模式:視野範圍內存在敵人,開始攻擊
            if self.units(UnitTypeId.STALKER).amount>5:
                if len(self.known_enemy_units)>0:
                    for s in self.units(UnitTypeId.STALKER).idle:
                        await self.do(s.attack(random.choice(self.known_enemy_units)))
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Medium)
    ], realtime=False)
    

    運行結果如下

    可以看到,4個折躍門訓練追獵者並發動進攻。

    擊敗困難電腦

    我們目前的代碼只能擊敗中等和簡單電腦,那麼如何擊敗困難電腦呢?
    代碼如下

    
    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    from sc2.constants import *
    import random
    
    
    class SentdeBot(sc2.BotAI):
        def __init__(self):
            # 經過計算,每分鐘大約165迭代次數
            self.ITERATIONS_PER_MINUTE = 165
            # 最大农民數量
            self.MAX_WORKERS = 65
    
        async def on_step(self, iteration: int):
            self.iteration = iteration
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
            await self.offensive_force_buildings()
            await self.build_offensive_force()
            await self.attack()
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞鈕*16(一個基地配備16個农民)大於農民數量並且現有农民數量小於MAX_WORKERS
            if len(self.units(UnitTypeId.NEXUS))*16>len(self.units(UnitTypeId.PROBE)) and len(self.units(UnitTypeId.PROBE))<self.MAX_WORKERS:
                    # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
                    for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                        # 是否有50晶體礦建造农民
                        if self.can_afford(UnitTypeId.PROBE):
                            await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene))
    
        ## 開礦
        async def expand(self):
            # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦
            if self.units(UnitTypeId.NEXUS).amount<self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford(UnitTypeId.NEXUS):
                await self.expand_now()
    
        ## 建造進攻性建築
        async def offensive_force_buildings(self):
            print(self.iteration / self.ITERATIONS_PER_MINUTE)
            if self.units(UnitTypeId.PYLON).ready.exists:
                pylon = self.units(UnitTypeId.PYLON).ready.random
                # 根據神族建築科技圖,折躍門建造過後才可以建造控制核心
                if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE):
                    if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
                        await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon)
                # 否則建造折躍門
                # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值
                elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                    if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
                        await self.build(UnitTypeId.GATEWAY, near=pylon)
                # 控制核心存在的情況下建造星門
                if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
                    if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                        if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE):
                            await self.build(UnitTypeId.STARGATE, near=pylon)
    
        ## 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue:
                if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount:
    
                    if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0:
                        await self.do(gw.train(UnitTypeId.STALKER))
    
            for sg in self.units(UnitTypeId.STARGATE).ready.noqueue:
                if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0:
                    await self.do(sg.train(UnitTypeId.VOIDRAY))
    
        ## 尋找目標
        def find_target(self,state):
            if len(self.known_enemy_units)>0:
                # 隨機選取敵方單位
                return random.choice(self.known_enemy_units)
            elif len(self.known_enemy_units)>0:
                # 隨機選取敵方建築
                return random.choice(self.known_enemy_structures)
            else:
                # 返回敵方出生點位
                return self.enemy_start_locations[0]
    
        ## 進攻
        async def attack(self):
            # {UNIT: [n to fight, n to defend]}
            aggressive_units = {UnitTypeId.STALKER: [15, 5],
                                UnitTypeId.VOIDRAY: [8, 3]}
    
            for UNIT in aggressive_units:
                # 攻擊模式
                if self.units(UNIT).amount > aggressive_units[UNIT][0] and self.units(UNIT).amount > aggressive_units[UNIT][
                    1]:
                    for s in self.units(UNIT).idle:
                        await self.do(s.attack(self.find_target(self.state)))
                # 防衛模式
                elif self.units(UNIT).amount > aggressive_units[UNIT][1]:
                    if len(self.known_enemy_units) > 0:
                        for s in self.units(UNIT).idle:
                            await self.do(s.attack(random.choice(self.known_enemy_units)))
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Hard)
    ], realtime=False)
    

    運行結果如下

    可以看到,擊敗了困難人族電腦,但是電腦選擇了rush戰術,我們寫得AI腳本會輸掉遊戲。顯然,這不是最佳方案。
    “只有AI才能拯救我的勝率”,請看下文。

    採集地圖數據

    這次我們只造一個折躍門,全力通過星門造虛空光輝艦
    修改offensive_force_buildings(self)方法的判斷

    elif len(self.units(GATEWAY)) < 1:
                    if self.can_afford(GATEWAY) and not self.already_pending(GATEWAY):
                        await self.build(GATEWAY, near=pylon)

    註釋或者刪除build_offensive_force(self)的建造追獵者的代碼

            ## 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            # for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue:
            #     if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount:
            #
            #         if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0:
            #             await self.do(gw.train(UnitTypeId.STALKER))
    
            for sg in self.units(UnitTypeId.STARGATE).ready.noqueue:
                if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0:
                    await self.do(sg.train(UnitTypeId.VOIDRAY))

    attack(self)中的aggressive_units註釋掉Stalker
    導入numpy和cv2庫

    game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8)

    建立以地圖Heigt為行,Width為列的三維矩陣

    for nexus in self.units(NEXUS):
                nex_pos = nexus.position
                print(nex_pos)
                cv2.circle(game_data, (int(nex_pos[0]), int(nex_pos[1])), 10, (0, 255, 0), -1)  # BGR

    遍歷星靈樞紐,獲取下一個位置,畫圓,circle(承載圓的img, 圓心, 半徑, 顏色, thickness=-1表示填充)
    接下來我們要垂直翻轉三維矩陣,因為我們建立的矩陣左上角是原點(0,0),縱坐標向下延申,橫坐標向右延申。翻轉之後就成了正常的坐標系。

    flipped = cv2.flip(game_data, 0)

    圖像縮放,達到可視化最佳。

            resized = cv2.resize(flipped, dsize=None, fx=2, fy=2)
            cv2.imshow('Intel', resized)
            cv2.waitKey(1)

    至此,完整代碼如下

    import sc2
    from sc2 import run_game, maps, Race, Difficulty
    from sc2.player import Bot, Computer
    from sc2.constants import *
    import random
    import numpy as np
    import cv2
    
    
    class SentdeBot(sc2.BotAI):
        def __init__(self):
            # 經過計算,每分鐘大約165迭代次數
            self.ITERATIONS_PER_MINUTE = 165
            # 最大农民數量
            self.MAX_WORKERS = 65
    
        async def on_step(self, iteration: int):
            self.iteration = iteration
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
            await self.offensive_force_buildings()
            await self.build_offensive_force()
            await self.intel()
            await self.attack()
    
        async def intel(self):
            # 根據地圖建立的三維矩陣
            game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8)
            for nexus in self.units(UnitTypeId.NEXUS):
                nex_pos = nexus.position
                # circle(承載圓的img, 圓心, 半徑, 顏色, thickness=-1表示填充)
                # 記錄星靈樞紐的位置
                cv2.circle(game_data, (int(nex_pos[0]), int(nex_pos[1])), 10, (0, 255, 0), -1)
            # 圖像翻轉垂直鏡像
            flipped = cv2.flip(game_data, 0)
            # 圖像縮放
            # cv2.resize(原圖像,輸出圖像的大小,width方向的縮放比例,height方向縮放的比例)
            resized = cv2.resize(flipped, dsize=None, fx=2, fy=2)
            cv2.imshow('Intel', resized)
    
            # cv2.waitKey(每Xms刷新圖像)
            cv2.waitKey(1)
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞鈕*16(一個基地配備16個农民)大於農民數量並且現有农民數量小於MAX_WORKERS
            if len(self.units(UnitTypeId.NEXUS)) * 16 > len(self.units(UnitTypeId.PROBE)) and len(
                    self.units(UnitTypeId.PROBE)) < self.MAX_WORKERS:
                # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
                for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                    # 是否有50晶體礦建造农民
                    if self.can_afford(UnitTypeId.PROBE):
                        await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0, nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0, vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR, vaspene))
    
        ## 開礦
        async def expand(self):
            # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦
            if self.units(UnitTypeId.NEXUS).amount < self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford(
                    UnitTypeId.NEXUS):
                await self.expand_now()
    
        ## 建造進攻性建築
        async def offensive_force_buildings(self):
            print(self.iteration / self.ITERATIONS_PER_MINUTE)
            if self.units(UnitTypeId.PYLON).ready.exists:
                pylon = self.units(UnitTypeId.PYLON).ready.random
                # 根據神族建築科技圖,折躍門建造過後才可以建造控制核心
                if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE):
                    if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
                        await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon)
                # 否則建造折躍門
                # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值
                # elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                elif len(self.units(UnitTypeId.GATEWAY)) < 1:
                    if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
                        await self.build(UnitTypeId.GATEWAY, near=pylon)
                # 控制核心存在的情況下建造星門
                if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
                    if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                        if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE):
                            await self.build(UnitTypeId.STARGATE, near=pylon)
    
        ## 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            for sg in self.units(UnitTypeId.STARGATE).ready.noqueue:
                if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0:
                    await self.do(sg.train(UnitTypeId.VOIDRAY))
    
        ## 尋找目標
        def find_target(self, state):
            if len(self.known_enemy_units) > 0:
                # 隨機選取敵方單位
                return random.choice(self.known_enemy_units)
            elif len(self.known_enemy_units) > 0:
                # 隨機選取敵方建築
                return random.choice(self.known_enemy_structures)
            else:
                # 返回敵方出生點位
                return self.enemy_start_locations[0]
    
        ## 進攻
        async def attack(self):
            # {UNIT: [n to fight, n to defend]}
            aggressive_units = {UnitTypeId.VOIDRAY: [8, 3]}
    
            for UNIT in aggressive_units:
                # 攻擊模式
                if self.units(UNIT).amount > aggressive_units[UNIT][0] and self.units(UNIT).amount > aggressive_units[UNIT][1]:
                    for s in self.units(UNIT).idle:
                        await self.do(s.attack(self.find_target(self.state)))
                # 防衛模式
                elif self.units(UNIT).amount > aggressive_units[UNIT][1]:
                    if len(self.known_enemy_units) > 0:
                        for s in self.units(UNIT).idle:
                            await self.do(s.attack(random.choice(self.known_enemy_units)))
    
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Hard)
    ], realtime=False)
    

    運行結果如下

    採集到了地圖位置。

    偵察

    在intel(self)里創建一個字典draw_dict,UnitTypeId作為key,半徑和顏色是value

    
            draw_dict = {
                UnitTypeId.NEXUS: [15, (0, 255, 0)],
                UnitTypeId.PYLON: [3, (20, 235, 0)],
                UnitTypeId.PROBE: [1, (55, 200, 0)],
                UnitTypeId.ASSIMILATOR: [2, (55, 200, 0)],
                UnitTypeId.GATEWAY: [3, (200, 100, 0)],
                UnitTypeId.CYBERNETICSCORE: [3, (150, 150, 0)],
                UnitTypeId.STARGATE: [5, (255, 0, 0)],
                UnitTypeId.ROBOTICSFACILITY: [5, (215, 155, 0)],
    
                UnitTypeId.VOIDRAY: [3, (255, 100, 0)]
            }

    迭代同上

    for unit_type in draw_dict:
                for unit in self.units(unit_type).ready:
                    pos = unit.position
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), draw_dict[unit_type][0], draw_dict[unit_type][1], -1)

    存儲三族的主基地名稱(星靈樞紐,指揮中心,孵化場),刻畫敵方建築。

    # 主基地名稱
            main_base_names = ["nexus", "supplydepot", "hatchery"]
            # 記錄敵方基地位置
            for enemy_building in self.known_enemy_structures:
                pos = enemy_building.position
                if enemy_building.name.lower() not in main_base_names:
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), 5, (200, 50, 212), -1)
            for enemy_building in self.known_enemy_structures:
                pos = enemy_building.position
                if enemy_building.name.lower() in main_base_names:
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), 15, (0, 0, 255), -1)

    刻畫敵方單位,如果是农民畫得小些,其他單位則畫大些。

            for enemy_unit in self.known_enemy_units:
    
                if not enemy_unit.is_structure:
                    worker_names = ["probe", "scv", "drone"]
                    # if that unit is a PROBE, SCV, or DRONE... it's a worker
                    pos = enemy_unit.position
                    if enemy_unit.name.lower() in worker_names:
                        cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (55, 0, 155), -1)
                    else:
                        cv2.circle(game_data, (int(pos[0]), int(pos[1])), 3, (50, 0, 215), -1)

    在offensive_force_buildings(self)方法中添加建造机械台

                if self.units(CYBERNETICSCORE).ready.exists:
                    if len(self.units(ROBOTICSFACILITY)) < 1:
                        if self.can_afford(ROBOTICSFACILITY) and not self.already_pending(ROBOTICSFACILITY):
                            await self.build(ROBOTICSFACILITY, near=pylon)

    創建scout(),訓練Observer

    async def scout(self):
            if len(self.units(OBSERVER)) > 0:
                scout = self.units(OBSERVER)[0]
                if scout.is_idle:
                    enemy_location = self.enemy_start_locations[0]
                    move_to = self.random_location_variance(enemy_location)
                    print(move_to)
                    await self.do(scout.move(move_to))
    
            else:
                for rf in self.units(ROBOTICSFACILITY).ready.noqueue:
                    if self.can_afford(OBSERVER) and self.supply_left > 0:
                        await self.do(rf.train(OBSERVER))

    生成隨機位置,很簡單。意思是橫坐標累計遞增-0.2和0.2倍的橫坐標,限制條件為如果x超過橫坐標,那麼就是橫坐標最大值。
    縱坐標同理。

        def random_location_variance(self, enemy_start_location):
            x = enemy_start_location[0]
            y = enemy_start_location[1]
    
            x += ((random.randrange(-20, 20))/100) * enemy_start_location[0]
            y += ((random.randrange(-20, 20))/100) * enemy_start_location[1]
    
            if x < 0:
                x = 0
            if y < 0:
                y = 0
            if x > self.game_info.map_size[0]:
                x = self.game_info.map_size[0]
            if y > self.game_info.map_size[1]:
                y = self.game_info.map_size[1]
    
            go_to = position.Point2(position.Pointlike((x,y)))
            return go_to

    完整代碼如下

    # -*- encoding: utf-8 -*-
    '''
    @File    :   demo.py
    @Modify Time      @Author       @Desciption
    ------------      -------       -----------
    2019/11/3 12:32   Jonas           None
    '''
    
    import sc2
    from sc2 import run_game, maps, Race, Difficulty, position
    from sc2.player import Bot, Computer
    from sc2.constants import *
    import random
    import numpy as np
    import cv2
    
    
    class SentdeBot(sc2.BotAI):
        def __init__(self):
            # 經過計算,每分鐘大約165迭代次數
            self.ITERATIONS_PER_MINUTE = 165
            # 最大农民數量
            self.MAX_WORKERS = 50
    
        async def on_step(self, iteration: int):
            self.iteration = iteration
            await self.scout()
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
            await self.offensive_force_buildings()
            await self.build_offensive_force()
            await self.intel()
            await self.attack()
    
        ## 偵察
        async def scout(self):
            if len(self.units(UnitTypeId.OBSERVER)) > 0:
                scout = self.units(UnitTypeId.OBSERVER)[0]
                if scout.is_idle:
                    enemy_location = self.enemy_start_locations[0]
                    move_to = self.random_location_variance(enemy_location)
                    print(move_to)
                    await self.do(scout.move(move_to))
    
            else:
                for rf in self.units(UnitTypeId.ROBOTICSFACILITY).ready.noqueue:
                    if self.can_afford(UnitTypeId.OBSERVER) and self.supply_left > 0:
                        await self.do(rf.train(UnitTypeId.OBSERVER))
    
        async def intel(self):
            game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8)
    
            # UnitTypeId作為key,半徑和顏色是value
            draw_dict = {
                UnitTypeId.NEXUS: [15, (0, 255, 0)],
                UnitTypeId.PYLON: [3, (20, 235, 0)],
                UnitTypeId.PROBE: [1, (55, 200, 0)],
                UnitTypeId.ASSIMILATOR: [2, (55, 200, 0)],
                UnitTypeId.GATEWAY: [3, (200, 100, 0)],
                UnitTypeId.CYBERNETICSCORE: [3, (150, 150, 0)],
                UnitTypeId.STARGATE: [5, (255, 0, 0)],
                UnitTypeId.ROBOTICSFACILITY: [5, (215, 155, 0)],
    
                UnitTypeId.VOIDRAY: [3, (255, 100, 0)],
                # OBSERVER: [3, (255, 255, 255)],
            }
    
            for unit_type in draw_dict:
                for unit in self.units(unit_type).ready:
                    pos = unit.position
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), draw_dict[unit_type][0], draw_dict[unit_type][1], -1)
    
            # 主基地名稱
            main_base_names = ["nexus", "supplydepot", "hatchery"]
            # 記錄敵方基地位置
            for enemy_building in self.known_enemy_structures:
                pos = enemy_building.position
                # 不是主基地建築,畫小一些
                if enemy_building.name.lower() not in main_base_names:
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), 5, (200, 50, 212), -1)
            for enemy_building in self.known_enemy_structures:
                pos = enemy_building.position
                if enemy_building.name.lower() in main_base_names:
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), 15, (0, 0, 255), -1)
    
            for enemy_unit in self.known_enemy_units:
    
                if not enemy_unit.is_structure:
                    worker_names = ["probe", "scv", "drone"]
                    # if that unit is a PROBE, SCV, or DRONE... it's a worker
                    pos = enemy_unit.position
                    if enemy_unit.name.lower() in worker_names:
                        cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (55, 0, 155), -1)
                    else:
                        cv2.circle(game_data, (int(pos[0]), int(pos[1])), 3, (50, 0, 215), -1)
    
            for obs in self.units(UnitTypeId.OBSERVER).ready:
                pos = obs.position
                cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (255, 255, 255), -1)
    
            # flip horizontally to make our final fix in visual representation:
            flipped = cv2.flip(game_data, 0)
            resized = cv2.resize(flipped, dsize=None, fx=2, fy=2)
    
            cv2.imshow('Intel', resized)
            cv2.waitKey(1)
    
        def random_location_variance(self, enemy_start_location):
            x = enemy_start_location[0]
            y = enemy_start_location[1]
    
            x += ((random.randrange(-20, 20)) / 100) * enemy_start_location[0]
            y += ((random.randrange(-20, 20)) / 100) * enemy_start_location[1]
    
            if x < 0:
                x = 0
            if y < 0:
                y = 0
            if x > self.game_info.map_size[0]:
                x = self.game_info.map_size[0]
            if y > self.game_info.map_size[1]:
                y = self.game_info.map_size[1]
    
            go_to = position.Point2(position.Pointlike((x, y)))
            return go_to
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞鈕*16(一個基地配備16個农民)大於農民數量並且現有农民數量小於MAX_WORKERS
            if len(self.units(UnitTypeId.NEXUS)) * 16 > len(self.units(UnitTypeId.PROBE)) and len(
                    self.units(UnitTypeId.PROBE)) < self.MAX_WORKERS:
                # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
                for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                    # 是否有50晶體礦建造农民
                    if self.can_afford(UnitTypeId.PROBE):
                        await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0, nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0, vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR, vaspene))
    
        ## 開礦
        async def expand(self):
            # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦
            if self.units(UnitTypeId.NEXUS).amount < self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford(
                    UnitTypeId.NEXUS):
                await self.expand_now()
    
        ## 建造進攻性建築
        async def offensive_force_buildings(self):
            print(self.iteration / self.ITERATIONS_PER_MINUTE)
            if self.units(UnitTypeId.PYLON).ready.exists:
                pylon = self.units(UnitTypeId.PYLON).ready.random
                # 根據神族建築科技圖,折躍門建造過後才可以建造控制核心
                if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE):
                    if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
                        await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon)
                # 否則建造折躍門
                # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值
                # elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                elif len(self.units(UnitTypeId.GATEWAY)) < 1:
                    if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
                        await self.build(UnitTypeId.GATEWAY, near=pylon)
                # 控制核心存在的情況下建造机械台
                if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
                    if len(self.units(UnitTypeId.ROBOTICSFACILITY)) < 1:
                        if self.can_afford(UnitTypeId.ROBOTICSFACILITY) and not self.already_pending(
                                UnitTypeId.ROBOTICSFACILITY):
                            await self.build(UnitTypeId.ROBOTICSFACILITY, near=pylon)
    
                # 控制核心存在的情況下建造星門
                if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
                    if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                        if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE):
                            await self.build(UnitTypeId.STARGATE, near=pylon)
    
        ## 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            # for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue:
            #     if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount:
            #
            #         if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0:
            #             await self.do(gw.train(UnitTypeId.STALKER))
    
            for sg in self.units(UnitTypeId.STARGATE).ready.noqueue:
                if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0:
                    await self.do(sg.train(UnitTypeId.VOIDRAY))
    
        ## 尋找目標
        def find_target(self, state):
            if len(self.known_enemy_units) > 0:
                # 隨機選取敵方單位
                return random.choice(self.known_enemy_units)
            elif len(self.known_enemy_units) > 0:
                # 隨機選取敵方建築
                return random.choice(self.known_enemy_structures)
            else:
                # 返回敵方出生點位
                return self.enemy_start_locations[0]
    
        ## 進攻
        async def attack(self):
            # {UNIT: [n to fight, n to defend]}
            aggressive_units = {UnitTypeId.VOIDRAY: [8, 3]}
    
            for UNIT in aggressive_units:
                # 攻擊模式
                if self.units(UNIT).amount > aggressive_units[UNIT][0] and self.units(UNIT).amount > aggressive_units[UNIT][
                    1]:
                    for s in self.units(UNIT).idle:
                        await self.do(s.attack(self.find_target(self.state)))
                # 防衛模式
                elif self.units(UNIT).amount > aggressive_units[UNIT][1]:
                    if len(self.known_enemy_units) > 0:
                        for s in self.units(UNIT).idle:
                            await self.do(s.attack(random.choice(self.known_enemy_units)))
    
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Hard)
    ], realtime=False)
    

    運行結果如下,紅色和粉紅色是敵方單位。

    創建訓練數據

    統計資源、人口和軍隊人口比,在intel方法添加如下代碼

            # 追蹤資源、人口和軍隊人口比
            line_max = 50
            mineral_ratio = self.minerals / 1500
            if mineral_ratio > 1.0:
                mineral_ratio = 1.0
    
            vespene_ratio = self.vespene / 1500
            if vespene_ratio > 1.0:
                vespene_ratio = 1.0
    
            population_ratio = self.supply_left / self.supply_cap
            if population_ratio > 1.0:
                population_ratio = 1.0
    
            plausible_supply = self.supply_cap / 200.0
    
            military_weight = len(self.units(UnitTypeId.VOIDRAY)) / (self.supply_cap - self.supply_left)
            if military_weight > 1.0:
                military_weight = 1.0
    
            # 农民/人口      worker/supply ratio
            cv2.line(game_data, (0, 19), (int(line_max * military_weight), 19), (250, 250, 200), 3)
            # 人口/200    plausible supply (supply/200.0)
            cv2.line(game_data, (0, 15), (int(line_max * plausible_supply), 15), (220, 200, 200), 3)
            # (人口-現有人口)/人口  population ratio (supply_left/supply)
            cv2.line(game_data, (0, 11), (int(line_max * population_ratio), 11), (150, 150, 150), 3)
            # 氣體/1500   gas/1500
            cv2.line(game_data, (0, 7), (int(line_max * vespene_ratio), 7), (210, 200, 0), 3)
            # 晶體礦/1500  minerals minerals/1500
            cv2.line(game_data, (0, 3), (int(line_max * mineral_ratio), 3), (0, 255, 25), 3)

    運行結果如下,左下角自上而下依次是“农民/人口”,“人口/200”,“(人口-現有人口)/人口”,“氣體/1500”,“晶體礦/1500”

    採集進攻行為數據,在attack方法中加入如下代碼

            if len(self.units(UnitTypeId.VOIDRAY).idle) > 0:
                choice = random.randrange(0, 4)
                target = False
                if self.iteration > self.do_something_after:
                    if choice == 0:
                        # 什麼都不做
                        wait = random.randrange(20, 165)
                        self.do_something_after = self.iteration + wait
    
                    elif choice == 1:
                        # 攻擊離星靈樞紐最近的單位
                        if len(self.known_enemy_units) > 0:
                            target = self.known_enemy_units.closest_to(random.choice(self.units(UnitTypeId.NEXUS)))
    
                    elif choice == 2:
                        # 攻擊敵方建築
                        if len(self.known_enemy_structures) > 0:
                            target = random.choice(self.known_enemy_structures)
    
                    elif choice == 3:
                        # 攻擊敵方出生位置
                        target = self.enemy_start_locations[0]
    
                    if target:
                        for vr in self.units(UnitTypeId.VOIDRAY).idle:
                            await self.do(vr.attack(target))
                    y = np.zeros(4)
                    y[choice] = 1
                    print(y)
                    self.train_data.append([y, self.flipped])

    輸出如下結果

    ···
    [1. 0. 0. 0.]
    [0. 0. 1. 0.]
    [0. 0. 0. 1.]
    [0. 0. 1. 0.]
    [1. 0. 0. 0.]
    ···

    為了使用self.flipped = cv2.flip(game_data, 0),修改

            flipped = cv2.flip(game_data, 0)
            resized = cv2.resize(flipped, dsize=None, fx=2, fy=2)

            self.flipped = cv2.flip(game_data, 0)
            resized = cv2.resize(self.flipped, dsize=None, fx=2, fy=2)

    init 方法添加do_something_after和train_data

        def __init__(self):
            self.ITERATIONS_PER_MINUTE = 165
            self.MAX_WORKERS = 50
            self.do_something_after = 0
            self.train_data = []

    採集攻擊數據的時候不需要畫圖,我們在類前加HEADLESS = False,intel方法代碼修改如下

            self.flipped = cv2.flip(game_data, 0)
    
            if not HEADLESS:
                resized = cv2.resize(self.flipped, dsize=None, fx=2, fy=2)
                cv2.imshow('Intel', resized)
                cv2.waitKey(1)

    加入on_end方法,只存儲勝利的數據,在和代碼同級目錄新建train_data文件夾

        def on_end(self, game_result):
            print('--- on_end called ---')
            print(game_result)
    
            if game_result == Result.Victory:
                np.save("train_data/{}.npy".format(str(int(time.time()))), np.array(self.train_data))

    完整代碼如下

    import os
    import time
    
    import sc2
    from sc2 import run_game, maps, Race, Difficulty, position, Result
    from sc2.player import Bot, Computer
    from sc2.constants import *
    import random
    import numpy as np
    import cv2
    
    HEADLESS = True
    # os.environ["SC2PATH"] = 'F:\StarCraft II'
    
    class SentdeBot(sc2.BotAI):
        def __init__(self):
            # 經過計算,每分鐘大約165迭代次數
            self.ITERATIONS_PER_MINUTE = 165
            # 最大农民數量
            self.MAX_WORKERS = 50
            self.do_something_after = 0
            self.train_data = []
    
        def on_end(self, game_result):
            print('--- on_end called ---')
            print(game_result)
    
            if game_result == Result.Victory:
                np.save("train_data/{}.npy".format(str(int(time.time()))), np.array(self.train_data))
    
        async def on_step(self, iteration: int):
            self.iteration = iteration
            await self.scout()
            await self.distribute_workers()
            await self.build_workers()
            await self.build_pylons()
            await self.build_assimilators()
            await self.expand()
            await self.offensive_force_buildings()
            await self.build_offensive_force()
            await self.intel()
            await self.attack()
    
        ## 偵察
        async def scout(self):
            if len(self.units(UnitTypeId.OBSERVER)) > 0:
                scout = self.units(UnitTypeId.OBSERVER)[0]
                if scout.is_idle:
                    enemy_location = self.enemy_start_locations[0]
                    move_to = self.random_location_variance(enemy_location)
                    print(move_to)
                    await self.do(scout.move(move_to))
    
            else:
                for rf in self.units(UnitTypeId.ROBOTICSFACILITY).ready.noqueue:
                    if self.can_afford(UnitTypeId.OBSERVER) and self.supply_left > 0:
                        await self.do(rf.train(UnitTypeId.OBSERVER))
    
        async def intel(self):
            game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8)
    
            # UnitTypeId作為key,半徑和顏色是value
            draw_dict = {
                UnitTypeId.NEXUS: [15, (0, 255, 0)],
                UnitTypeId.PYLON: [3, (20, 235, 0)],
                UnitTypeId.PROBE: [1, (55, 200, 0)],
                UnitTypeId.ASSIMILATOR: [2, (55, 200, 0)],
                UnitTypeId.GATEWAY: [3, (200, 100, 0)],
                UnitTypeId.CYBERNETICSCORE: [3, (150, 150, 0)],
                UnitTypeId.STARGATE: [5, (255, 0, 0)],
                UnitTypeId.ROBOTICSFACILITY: [5, (215, 155, 0)],
    
                UnitTypeId.VOIDRAY: [3, (255, 100, 0)],
                # OBSERVER: [3, (255, 255, 255)],
            }
    
            for unit_type in draw_dict:
                for unit in self.units(unit_type).ready:
                    pos = unit.position
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), draw_dict[unit_type][0], draw_dict[unit_type][1], -1)
    
            # 主基地名稱
            main_base_names = ["nexus", "supplydepot", "hatchery"]
            # 記錄敵方基地位置
            for enemy_building in self.known_enemy_structures:
                pos = enemy_building.position
                # 不是主基地建築,畫小一些
                if enemy_building.name.lower() not in main_base_names:
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), 5, (200, 50, 212), -1)
            for enemy_building in self.known_enemy_structures:
                pos = enemy_building.position
                if enemy_building.name.lower() in main_base_names:
                    cv2.circle(game_data, (int(pos[0]), int(pos[1])), 15, (0, 0, 255), -1)
    
            for enemy_unit in self.known_enemy_units:
    
                if not enemy_unit.is_structure:
                    worker_names = ["probe", "scv", "drone"]
                    # if that unit is a PROBE, SCV, or DRONE... it's a worker
                    pos = enemy_unit.position
                    if enemy_unit.name.lower() in worker_names:
                        cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (55, 0, 155), -1)
                    else:
                        cv2.circle(game_data, (int(pos[0]), int(pos[1])), 3, (50, 0, 215), -1)
    
            for obs in self.units(UnitTypeId.OBSERVER).ready:
                pos = obs.position
                cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (255, 255, 255), -1)
    
    
            # 追蹤資源、人口和軍隊人口比
            line_max = 50
            mineral_ratio = self.minerals / 1500
            if mineral_ratio > 1.0:
                mineral_ratio = 1.0
    
            vespene_ratio = self.vespene / 1500
            if vespene_ratio > 1.0:
                vespene_ratio = 1.0
    
            population_ratio = self.supply_left / self.supply_cap
            if population_ratio > 1.0:
                population_ratio = 1.0
    
            plausible_supply = self.supply_cap / 200.0
    
            military_weight = len(self.units(UnitTypeId.VOIDRAY)) / (self.supply_cap - self.supply_left)
            if military_weight > 1.0:
                military_weight = 1.0
    
            # 农民/人口      worker/supply ratio
            cv2.line(game_data, (0, 19), (int(line_max * military_weight), 19), (250, 250, 200), 3)
            # 人口/200    plausible supply (supply/200.0)
            cv2.line(game_data, (0, 15), (int(line_max * plausible_supply), 15), (220, 200, 200), 3)
            # (人口-現有人口)/人口  population ratio (supply_left/supply)
            cv2.line(game_data, (0, 11), (int(line_max * population_ratio), 11), (150, 150, 150), 3)
            # 氣體/1500   gas/1500
            cv2.line(game_data, (0, 7), (int(line_max * vespene_ratio), 7), (210, 200, 0), 3)
            # 晶體礦/1500  minerals minerals/1500
            cv2.line(game_data, (0, 3), (int(line_max * mineral_ratio), 3), (0, 255, 25), 3)
    
    
    
    
            # flip horizontally to make our final fix in visual representation:
            self.flipped = cv2.flip(game_data, 0)
    
            if HEADLESS:
                resized = cv2.resize(self.flipped, dsize=None, fx=2, fy=2)
    
                cv2.imshow('Intel', resized)
                cv2.waitKey(1)
    
        def random_location_variance(self, enemy_start_location):
            x = enemy_start_location[0]
            y = enemy_start_location[1]
    
            x += ((random.randrange(-20, 20)) / 100) * enemy_start_location[0]
            y += ((random.randrange(-20, 20)) / 100) * enemy_start_location[1]
    
            if x < 0:
                x = 0
            if y < 0:
                y = 0
            if x > self.game_info.map_size[0]:
                x = self.game_info.map_size[0]
            if y > self.game_info.map_size[1]:
                y = self.game_info.map_size[1]
    
            go_to = position.Point2(position.Pointlike((x, y)))
            return go_to
    
        # 建造农民
        async def build_workers(self):
            # 星靈樞鈕*16(一個基地配備16個农民)大於農民數量並且現有农民數量小於MAX_WORKERS
            if len(self.units(UnitTypeId.NEXUS)) * 16 > len(self.units(UnitTypeId.PROBE)) and len(
                    self.units(UnitTypeId.PROBE)) < self.MAX_WORKERS:
                # 星靈樞紐(NEXUS)無隊列建造,可以提高晶體礦的利用率,不至於佔用資源
                for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue:
                    # 是否有50晶體礦建造农民
                    if self.can_afford(UnitTypeId.PROBE):
                        await self.do(nexus.train(UnitTypeId.PROBE))
    
        ## 建造水晶
        async def build_pylons(self):
            ## 供應人口和現有人口之差小於5且建築不是正在建造
            if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON):
                nexuses = self.units(UnitTypeId.NEXUS).ready
                if nexuses.exists:
                    if self.can_afford(UnitTypeId.PYLON):
                        await self.build(UnitTypeId.PYLON, near=nexuses.first)
    
        ## 建造吸收廠
        async def build_assimilators(self):
            for nexus in self.units(UnitTypeId.NEXUS).ready:
                # 在瓦斯泉上建造吸收廠
                vaspenes = self.state.vespene_geyser.closer_than(15.0, nexus)
                for vaspene in vaspenes:
                    if not self.can_afford(UnitTypeId.ASSIMILATOR):
                        break
                    worker = self.select_build_worker(vaspene.position)
                    if worker is None:
                        break
                    if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0, vaspene).exists:
                        await self.do(worker.build(UnitTypeId.ASSIMILATOR, vaspene))
    
        ## 開礦
        async def expand(self):
            # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦
            if self.units(UnitTypeId.NEXUS).amount < self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford(
                    UnitTypeId.NEXUS):
                await self.expand_now()
    
        ## 建造進攻性建築
        async def offensive_force_buildings(self):
            # print(self.iteration / self.ITERATIONS_PER_MINUTE)
            if self.units(UnitTypeId.PYLON).ready.exists:
                pylon = self.units(UnitTypeId.PYLON).ready.random
                # 根據神族建築科技圖,折躍門建造過後才可以建造控制核心
                if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE):
                    if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
                        await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon)
                # 否則建造折躍門
                # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值
                # elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                elif len(self.units(UnitTypeId.GATEWAY)) < 1:
                    if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
                        await self.build(UnitTypeId.GATEWAY, near=pylon)
                # 控制核心存在的情況下建造机械台
                if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
                    if len(self.units(UnitTypeId.ROBOTICSFACILITY)) < 1:
                        if self.can_afford(UnitTypeId.ROBOTICSFACILITY) and not self.already_pending(
                                UnitTypeId.ROBOTICSFACILITY):
                            await self.build(UnitTypeId.ROBOTICSFACILITY, near=pylon)
    
                # 控制核心存在的情況下建造星門
                if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
                    if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2):
                        if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE):
                            await self.build(UnitTypeId.STARGATE, near=pylon)
    
        ## 造兵
        async def build_offensive_force(self):
            # 無隊列化建造
            # for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue:
            #     if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount:
            #
            #         if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0:
            #             await self.do(gw.train(UnitTypeId.STALKER))
    
            for sg in self.units(UnitTypeId.STARGATE).ready.noqueue:
                if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0:
                    await self.do(sg.train(UnitTypeId.VOIDRAY))
    
        ## 尋找目標
        def find_target(self, state):
            if len(self.known_enemy_units) > 0:
                # 隨機選取敵方單位
                return random.choice(self.known_enemy_units)
            elif len(self.known_enemy_units) > 0:
                # 隨機選取敵方建築
                return random.choice(self.known_enemy_structures)
            else:
                # 返回敵方出生點位
                return self.enemy_start_locations[0]
    
        ## 進攻
        async def attack(self):
            if len(self.units(UnitTypeId.VOIDRAY).idle) > 0:
                choice = random.randrange(0, 4)
                target = False
                if self.iteration > self.do_something_after:
                    if choice == 0:
                        # 什麼都不做
                        wait = random.randrange(20, 165)
                        self.do_something_after = self.iteration + wait
    
                    elif choice == 1:
                        # 攻擊離星靈樞紐最近的單位
                        if len(self.known_enemy_units) > 0:
                            target = self.known_enemy_units.closest_to(random.choice(self.units(UnitTypeId.NEXUS)))
    
                    elif choice == 2:
                        # 攻擊敵方建築
                        if len(self.known_enemy_structures) > 0:
                            target = random.choice(self.known_enemy_structures)
    
                    elif choice == 3:
                        # 攻擊敵方出生位置
                        target = self.enemy_start_locations[0]
    
                    if target:
                        for vr in self.units(UnitTypeId.VOIDRAY).idle:
                            await self.do(vr.attack(target))
                    y = np.zeros(4)
                    y[choice] = 1
                    print(y)
                    self.train_data.append([y, self.flipped])
    
    
    ## 啟動遊戲
    run_game(maps.get("AcidPlantLE"), [
        Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Medium)
    ], realtime=False)
    

    可以看到train_data文件夾下存儲了勝利數據

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整