t-hom’s diary

主にVBAネタを扱っているブログです。

VBA クラスモジュールで工場建造ゲームSatisfactoryのコンベアスプリッター効率検証

最近遊んでいる工場建造ゲーム Satisfactoryで少し疑問に思うことがあったので、今回はVBAを使って検証してみた。
ゲームを題材にしてはいるものの、この記事で伝えたいことはクラスモジュールの有用性なので、ゲーム自体に興味ない方も是非ご一読いただければ幸いである。

前提の説明

このゲームではコンベアスプリッターという機材を使ってベルトコンベアを分岐させ、複数のマシンに資源を送り込むことができる。
スプリッターは資源の分配レートを指定できない為、出力経路が2本なら単純に2分割、3本なら3分割される仕組みである。
この時に使用する分岐メソッドは2タイプあり、1つはLoad Balancing Method、もう1つはOverflow Methodである。

実際のゲーム画面で説明するとこんな感じ。

モデル化したものがこちらである。

Load Balancing Methodの方は最初から資源が等配分されるのに対し、Overflow MethodはInputに近い順から配分が大きくなる。

Overflowメソッドで受け取れる資源は次のようになる。

No 受け取れる資源の量
マシン1 半分
マシン2 半分の半分
マシン3 半分の半分の半分
マシン4 残り

一見、後方のマシンの生産性が低いように思えるが、マシン1には生産効率を超えて資源が入るので稼働を続けると貯めきれなくなった分が溢れて資源を受け取れなくなる。そうすると受け取れなかった分は次のマシンに回されていくので、同じ資源を与えた場合のトータルの生産性はLoad Balancing Methodに追いつくとされている。(立ち上がりのアドバンテージによって総生産数の差は埋まらないが、このゲームではマシンは延々と動き続けるので総生産数はどのみち∞。重要なのは生産効率である。)

Overflowメソッドの場合、同じパターンで何台でも繋げられるので拡張性に優れている。

疑問:本当に生産効率は変わらないのか

稼働から一定時間が経過すると2つのメソッドの生産性はイコールになるということだが、本当にそうなのか心配になった。
生産性がイコールになるという説明は頭では理解できる。しかし直感的には資源が常に等分されるLoad Balancingの方が生産性に優れているような気がするのだ。

大規模な工場を建造する場合は失敗するとかなりの時間を失うため、ここでこの疑問をばっちり解消しておきたい。

VBAで検証:設計編

今回Load Balancingメソッドについては特に検証はしない。
資源が等しく配分される以上、検証するまでもなく全マシンの生産性は最初から最大である。

検証するのはOverflowメソッドの生産性である。

コンベアスプリッターとマシンで共通して使えるノードクラスを考え、次のプロパティを実装することにした。
プロパティはパブリック変数として簡易に実装する。

変数名 説明
Storage Double 格納できる資源の上限数
Store Double 実際に格納されている資源の数
Consume Double 1チックごとに消費する資源の数
Product Long 生産された製品の数
NextNodes Collection 資源の送出先ノード

スプリッターの場合はConsumeは0、資源の上限数は現状コンベアベルトMk.5の運搬スピードが780 Item/minなので780する。NextNodesには次のスプリッターノードとマシンノードを登録する。
マシンの場合はConsumeが90、資源の上限数は500とし、NextNodesは何も登録しない。

また、マシンの挙動としてStoreがConsumeよりも大きい場合、1チックごとにStoreからConsumeを差し引いて、代わりにProductが1増える仕組みとした。
ちなみにスプリッターも内部的には同じ挙動をするが、Consumeは0なのでStoreは減らず、単にProductだけが増え続ける。スプリッターのProduct数に意味はないが、わざわざ増やさない処理を書くのも面倒だったので単に無視することにした。

VBAで検証:コーディング

まずはクラスモジュールを挿入し、オブジェクト名をNodeとして次のコードを張り付ける。

Option Explicit
Public Storage As Double
Public Store As Double
Public Consume As Double
Public Product As Long
Public NextNodes As Collection


Public Sub Tick()
    Dim n As Node
    
    If Store > Consume Then
        Store = Store - Consume
        Product = Product + 1
    End If
    
    Dim cnt As Integer
    cnt = NextNodes.Count
    For Each n In NextNodes
        Dim ret As Double
        ret = n.TryReceive(Store / cnt)
        Store = Store - (Store / cnt)
        Store = Store + ret
        cnt = cnt - 1
    Next
End Sub

Public Function TryReceive(amount As Double) As Double
    Store = Store + amount
    Dim ret As Double
    If Storage < Store Then
        ret = Store - Storage
        Store = Storage
    Else
        ret = 0
    End If
    TryReceive = ret
End Function

Private Sub Class_Initialize()
    Set NextNodes = New Collection
End Sub


次に標準モジュールのコード。

Sub OverflowProductivityCheck()
    Const NUMBER_OF_TICKS = 100
    Dim splitter(1 To 4) As Node
    Dim machine(1 To 4) As Node
    
    Dim i As Long
    For i = 1 To 4
        Set splitter(i) = New Node
        splitter(i).Storage = 780
        splitter(i).Consume = 0
        
        Set machine(i) = New Node
        machine(i).Storage = 500
        machine(i).Consume = 90
    Next
    
    For i = 1 To 4
        splitter(i).NextNodes.Add machine(i)
        If i < 4 Then
            splitter(i).NextNodes.Add splitter(i + 1)
        End If
    Next
    
    Dim j As Integer
    For i = 1 To NUMBER_OF_TICKS
        splitter(1).TryReceive 360
        For j = 1 To 4
            splitter(j).Tick
            machine(j).Tick
        Next
    Next
    
    For i = 1 To 4
        Debug.Print machine(i).Product
    Next
End Sub

OverflowProductivityCheckプロシージャはNUMBER_OF_TICKSで指定された回数分時間を進めて、マシンごとの累計プロダクト数を出力する。

NUMBER_OF_TICKSを変えて何度か試してみたところ、Tick回数ごとの累計プロダクト数は以下のようになった。

↓マシン/Tick回数→ 5 10 50 100 10000
マシン1 5 10 50 1000 10000
マシン2 4 9 49 99 9999
マシン3 2 6 45 95 9995
マシン4 2 6 45 95 9995

この実験で、累計プロダクト数は立ち上がり時の効率による影響を受けているものの、生産性は確かにLoad Balancing Methodに追いつくということが分かった。

終わりに

本当はクラスなんて作らなくても数学的に解けないか、あるいはExcelの機能で簡単に解けないかということも考えていたのだが、結局私は文系思考から抜け出せていない為、ロジックを言語で記述すること(つまりプログラミング)で実験することにした。

私の中に最近、ロジックを式で完結に表記するのが理系、言葉で順序だてて説明するのが文系というイメージがある。そう考えると、プログラミングは文系最強の道具なのではないかと思う。

さて、今回は頭で考えてもなかなか払拭できない疑問を晴らすためにクラスモジュールを使って実際の構造を再現して実行してみた。
頭の中に描いたモデルを実際にプログラミングで使える道具にしてしまうのがクラスモジュールの強みである。

特にデータ構造を作ったとき、各ノードは何らかの機能を持っていることが普通のなので、クラスを用いることでそうしたモデルを直感的に扱うことができるようになる。

クラスなんて使わなくてもVBAは書けるので全く覚える必要はないという意見も耳にするが、それは頭の良い人が言ってることなので真に受けないほうが良い。
絵を描く天才が、直線引くのに定規なんて要ります?鉛筆だけで十分でしょと言ってるのと同じ。

私は凡人である。凡人には強力なツールが必要だ。さもなければすぐに頭がこんがらがってしまい、難しい問題に太刀打ちできない。
皆さんも凡人である。天才はこんなブログ読んでない。多分。

クラスモジュールは凡人にこそおススメしたい、VBA最強の整理整頓ツールである。

以上。

thom.hateblo.jp

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。