factorio 7セグ表示を作る

  • by

factorioの紹介

factorioはsteamで公開されているシミュレーションゲームの一つです。とある惑星に不時着した主人公がその星にある資源を元に工業化を進めていき、最終的にロケットを打ち上げることが目的となっています。工場を効率的に運用するために様々な工夫を凝らすことができるとても楽しいゲームです。

factorio内では生産や資源の採取、あるいは外敵の駆除のためのアイテムのみならず、より複雑な挙動を実装するための回路というアイテム群が存在します。これら回路パーツや他アイテムなどを接続し、形成したネットワークを回路ネットワークと呼びます。本記事ではこの回路ネットワークを用いた応用例として7セグメントディスプレイの実装にトライしてみました。

7セグメント

Fig.1 7セグメントで表示した「27」

0~9のアラビア数字を表示する方法として7セグメント、7つの要素の点滅の組み合わせを使う方法があります。

factorioにおける回路ネットワークはプログラミングのように冗長な箇所がいくつもあるので、同じような機能を実装するとしても複数の方法があります。今回説明する内容はその中のひとつととらえていただき、より効率的、もしくは幅広く応用ができる方法があればお教えいただけると幸いです。

回路ネットワークの基本

Fig. 2 ランプを点灯

おそらく7セグ表示についても回路ネットワークについても弊記事よりもよく解説されているwebページがあるかもしれませんが、屋上屋を重ねるとしても備忘録として書こうかと思います。

factorioにおける回路ネットワークはケーブル(グリーンケーブル、レッドケーブル)を接続することで構成できます。

Fig. 3 定数回路でランプを点灯

Fig. 3 はFig. 2でランプを点灯する過程を解説したものです。

定数回路からは緑シグナルで1という値を送信しています。このシグナルは変数として理解するのが都合がいいですが、一方で代入や初期値の設定などができない(単純にはできない)ので、プログラミングにおける変数とまったく同じ概念ではないようです。

一方ランプの側では点灯する条件を「緑シグナルの値が0より大きい」としています。今同じ回路ネットワーク内での緑シグナルの値は1ですので0より大きいという条件を満たします(=光る)。

複数ランプを扱う

複数ランプを接続し、条件によって点灯するランプを決めることは難しくありません(Fig. 4, 5)。

Fig. 4 3つのランプを条件によって点灯し分ける
Fig. 5 Fig. 4の回路ネットワークを模式的に表したもの

あるいは大小の条件と液体ネットワークのタンクと接続することで、タンク内の液体残量に合わせてランプを点灯させることもできます(Fig. 6)。

Fig.6 各タンク残量をランプで表した様子

しかし7セグ表示となるとこの方式(光るランプを直接指定する方法)はかなりきついです。もう少し工夫が必要です。

2進法を使う

7セグ表示、あるいはより大きい解像度のディスプレイを作る場合、2進法を使う必要があります。具体的には2進法表示したときの桁数が各ディスプレイの表示する位置(素子)、そして値の0が消灯1が点灯、となるようにしてあげると比較的すっきり実装できます。

単純な例としてランプ2つの場合を考えていきます。ランプ2つだと…

\[2^2=4\]

4通りの点灯のパターンがあります。

10進数2進数ランプ1(上段)ランプ2(下段)
000消灯消灯
101消灯点灯
210点灯消灯
311点灯点灯
Table 1 数値とランプの消灯/点灯の関係
Fig. 7 2つのランプの点灯パターン

Table 1の挙動をfactorio内で実装した様子をFig. 7で示しています。この回路を今後、便宜のため「2ランプ点灯回路」と呼びます。

この挙動は算術回路を用いて実装することができます。具体的には算術回路のAND演算によるマスク、ビットシフトによる値の取り出しです。

Fig. 8にあるような2ランプ点灯回の模式図をもとに説明を進めます。

Fig. 8 2ランプ点灯回路の模式図

ここで定数回路の出力は緑シグナルの2にしました。factorio上では10進数の値のみ扱えるのでここでいう2はもちろん10進数での表記になります。2進数にすると10で表されます。

AND演算を施すことで特定の桁の値を取り出すことができます。

Fig. 9 2ランプ点灯回路における1桁目を取り出す過程

入力である10と01でAND演算(2進数なので各桁同士の掛け算と同値)をおこなうと00になります。

この値を緑シグナルとして出力すると下の段のランプは点灯しません。

Fig. 10 2ランプ点灯回路における2桁目を取り出す過程

同様に入力の10と10でAND演算をおこなうと10が出力されます。

Fig. 11 2ランプ点灯回路における2桁目の値をシフトする過程

さらに2桁目の値だけを抜き出して0or1で表すことを考えると桁をずらす必要があります。ここでFig. 11のようにシフト演算をしてあげれば特定の桁(この例では2桁目)の値だけを出力することができます。

点灯パターンの制御

先ほど用いた2ランプ点灯回路では点灯パターンは4つしかありませんでした。さて、7セグメントでは点灯するパターンはいくつあるでしょうか?答えは…

\[2^7=128\]

128通りです。そして、その中で使いたい点灯パターンは0~9の10個です。

ここでは使いたい点灯パターンとその指令の出し方について説明します。

また説明のために簡単な例を用いていきます。先ほどの「2ランプ点灯回路」に倣って「4ランプ点灯回路」としましょう。使うランプが4つあるので、点灯パターンは…

\[2^4=16\]

16通りです。

実装にあたって4つのランプ(2進数表示では4桁の値)を扱うため、2ランプ点灯回路を拡張します。

Fig. 12 4ランプ点灯回路実装例(入力11の時)

値の桁数とランプの対応はFig. 13、使う点灯パターンをFig. 14に示します。

Fig. 13 桁数とランプの対応(4ランプ点灯回路)
Fig. 14 使う点灯パターン(4ランプ点灯回路)

ここでの問題は、例えばFig. 14における①のようなパターンで点灯させるにはどのような値を出せばよいか、というものです。この答は非常に単純にとくことができます。

①の場合を考えていきましょう。①のパターンでは左下が消灯、つまり0で他は1です。Fig. 13と照らし合わせてみると左下は2桁目にあたります。これらを合わせると2桁目のみが0で他は1、最終的に導かれる値は「1101」となります。

他も同様に②なら「0111」、③は「1011」、そして④は「1110」のように求めることができます。factorioでは2進数表記のまま扱えないので10進数に変換する必要があります。任意の変換できるもの(電卓、紙と鉛筆、脳、プログラム、webサイト)を使っていきましょう。

例えばcman.jpの変換を利用して①の値を確認すると…

Fig. 15 2進数から10進数へのwebサイト上(cman.jp)での変換

2進数での「1101」は10進数での「13」ということがわかりました。

同様に他のパターンも10進数での値を求め緑シグナルとして送信するとFig. 15~18のような結果が得られます。

Fig. 15 パターン①1101 → 出力信号: 13
Fig. 16 パターン②0111 → 出力信号: 7
Fig . 17 パターン③1011 → 出力信号: 11
Fig. 18 パターン④1110 → 出力信号: 14

7セグの実装

ここまででかなり7セグメント表示に近づけたと思います。ここで実装までの流れを確認しておきましょう。

  1. ランプの配置(それぞれ何桁目にあたるか割り振る)
  2. マスク、ビットシフト回路を設置する
  3. アラビア数字のような点灯パターンと合致する指令値(10進数)を計算する
  4. 0~9の入力に対して3で求めた値が出力されるようにする

上のリストで示した通り、順を追ってやっていきます。

ランプの配置

Fig. 19 ランプの配置と桁数の指定

桁数の指定自体は後々の回路の設定と整合がとれていれば、実装する人の好みで決めて良いかと思います。

マスク・ビットシフト回路の設置

Fig. 20 算術回路の配置と接続(左: factorio画面、右: 模式図)

ランプは全て同じ点灯条件にしておきます。

点灯パターンと数値

ランプの配置を見ながら2進数での指令値を求め、その後に10進数に変換するとスムーズにおこなえると思います。ここの表の値はあくまでFig. 19の配置に則ったものであり、自分で変えた場合には別途計算する必要があります。

数字パターン2進数10進数
01110111119
1001001018
2101110193
3101101191
4011101058
51101011107
61101111111
71110010114
81111111127
91111011123
Table 2 点灯する数字のパターンと指令値の対応

指令値の出力

さてここで0~9に対応する10進数での指令値が求まったので、出力する方法を考えていきましょう。

必要となるのは条件回路と算術回路です。

例えば表示する対象を鋼鉄製チェスト内のインサータの数としましょう。そして現在は2個入っているとします。この時にパターン2と対応する93を出力し、Fig. 20の回路に入力できるようにしたいです。

Fig. 21 インサータの個数2から93を出力する回路例

その機能を実装する例としてFig. 21の回路を挙げます。条件回路で2と等しいときに2シグナルとして1を送信します。この「2シグナル」というのは「緑シグナル」や「インサータシグナル」と同様のシグナルの種類であり、値をひとつ設定できます。条件回路の設定で、条件を満たす場合に所定のシグナルで1を出力するように設定します。そして2シグナルの値×93を実行すると2シグナルの値は1なので93が出力されます。

この回路でインサータの数が2でなかった場合を考えてみましょう。インサータの数が2ではなかった場合は、条件を満たさないので2シグナルは0となります。算術回路では0×93を計算し0が出力されます。

このFig. 21の回路を0~9の10種類用意すると7セグ回路は完成します。

Fig. 22 アイテム数から指令値を出力する回路(模式図)
Fig. 23 アイテム数から指令値を出力する回路(factorio画面)

7セグ表示の完成

現段階での完成品の模式図をFig. 24に、factorio画面をFig. 25に示します。

Fig. 24 7セグ表示回路模式図
Fig. 25 7セグ表示回路(factorio画面)

実際の挙動(YouTube)

Video 1 7-segment display demo

応用

実際の挙動をご覧になった方はわかると思いますが0~9の範囲を出ると機能しません。実用的なものとするために、とってきた値を10進数での桁ごとに分解して、表示する必要があります。

あるいは見やすいように1つのセグメントをもっと大きくしたり、何かの個数だけでなく液体の量を扱えるようにしたり、あるいはクロックと合わせてプレイ時間を表示したり色々できると思います。

応用例1 タンク残量の%表示

Fig. 26 7セグメント表示回路を応用した原油残量表示

応用例2 プレイ時間の表示

Video 2 7-segment play time display

まとめ

実は、思い出したようにチャレンジしては完成できず…、というのを何度か繰り返して今の形になりました。最終的にたどり着いたのが本来の7セグの運用に近くなったのはもはや必然と言ってもいいでしょう。

今後は、上で挙げた応用例の記事化、ディスプレイ化、回路ネットワークを利用したゲーム攻略、その他もろもろなんかに取り組んでいきたいと思います。

おまけ

今回作成した7セグ表示のコード

0eNrdnNtq4zoUQH9l0OOQDNbFFwVmYOA8nx84lOAkaivwDVspJxT/+8hJr75JsoXlzkvBib2t7JUtay87fQaH5MyKkmcC7J4BP+ZZBXb/PYOKP2Rx0rwmLgUDO8AFS8EGZHHabMUlF48pE/y4PebpgWexyEtQbwDPTux/sIP1RhnjxI78xMr+AKi+2wCWCS44u43ounHZZ+f0wEp5hrc4lWAs2R4fWSVk8CKv5DF51pxWxtnCCP/wN+AiQ1L6w5dnkJ8xY8dmn6rZCTZ/HkrGso+n4Sd5gNyXl8czF9dNWN/VdfO5WiNBY5+ob0DwZTjRdTgnXt5GczthnokyT/YH9hg/cRlAHvUSdi/fO/G3cd/zshJ7RY55VrFSsGtmG7YibkDTZiMt4vI6xB34KQ/Iz6I498R74qU4y1fe033dY0tvIYuLHNY5E/v7Mk/3PJMxwO4+TipWm6Qafshrs427qd8ANHh4a3fUikbab/eDxFNBUlcgIwsgI6sgOzXzmQT0zcCicbDtcENkiSHZkLouUWKBLLFK1i45TXD+VHDOShJbAIetgiOKkozMwBJFSUZ6ZAPFWqIPbmAI9z2yHt/RKbJiTYz9O2mIQom2YJL0dRDg+wTUtxTXRkDDcaDIMwMajgNthxsCGs4AGi0MlA4AxW6ABmbAgnFgmgUYTeBFHPEivbz8yA0uarW+qGI+9fS6EDoD59LzKe7FSaEbnJFiOoVmuCPFdAr1yrPhbgwUOqrPl7T3QA3IZ6i///3HDdZOXRlihZo2AMIZ3OhquGHkilt3wmsVEDEEhzXBIXNwAV1fwX3m9uuXI2xIhYUMccAzOKyngHxXHKAZBxQOcXiXJlUaJ8k2idOiN/l+V7B2Ev46pLdsz0n2hyV7p9uWb58rJs+T5KVMlSjPzCh9SPPy7E+ThdjzXJmJ0IKZCK2aCawwE4FVCwwDTbTBVLTQFdrAAtrArtD3x9liQ0nRNb3j8QbhhtOMokO40AJcaBeuomXCpmtrxVod6/ZM0VS4ziZlZAEusgtXoRcN7wMo6haGmmjpZL2oX7q2VoVBv16EjgQHUnRUGBne2vEU5aq5hkLeDKbewkzDAaZkJcq4VVaGjhIqLKTm0gnByRZyeaKo30LilUjllvUwrFEFT6RbomgG0KWnXdhfoo7uEnS9cWuWNHzGpSuOx+MNIsWTBeXySIf9SuTKTyLF/XRs+qCEr/mYGZnBzVsNNxg4A6fSDYZdCwo0wfmTheaaCg47EpoIGwrNaIhDMIPDegqIuOJADDnQIQ6hrlgOXsTya/q/tFjWvTxHptr9dZL4ytlBms8DIqqbHfIXfXeQZvuFvanmGrnyX74F/+Xb9V+BZbmpCw9OlZfO4HkW4HmLykvj2w6aehKjGSoLLby28PsX5p6jx1khtdsod13WADM8w20szcwb0I/UDTOM7OoqXR+F5zTBeDVreGcPV2HFbVlieHHDmj0w9mdwQ2vtvRbkppBOyPQHc6Emtzk983rqDTrqmbFhz0y8IQ7hDA5orfPechx8Mw54yCHhyNxdoC/ff2LdVTA1dxf4y2eHaD6dQjxzd/EXfHc0218CZ6wQyFqvNC5XCIop7qZn7za3Tn/34R9BbEASH1hyHU9aJOxbuK3YQyoP/nbiVZHEF7nLEyurW6ojSEKKQoIDQlFU138APS69Sw==

広告

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です