白黒地帯

ゲームとか色々、Twitterに書ききれないことなど。

【shapez】回路いろいろ

めためたにハマってしまいましたshapez。
factorioで工場系ゲームに興味を持った人は多いと思うんですがこちらもオススメしたい。

実は2年前にも1度やっていて、そのときもうすでに完成された面白さだな…と思っていたのですが
2年の時を経て、なかなか便利だったり工場を綺麗に作れるパーツが追加されていました。
そして何より大きい追加要素である回路(wires)!
今回はその回路で色々遊んだ記録になります。

色々作りすぎてまとまってなかったり、ツイートしたもの貼っただけとかもあるので
あとから整理・追加するかもしれません。
回路でなく実工場の諸々もまとめたいですしね。

目次

ショート(輻輳、ワイヤーエラー)検出器

異なる出力が1つのワイヤーに入ってしまった時にshapezではワイヤーが赤色になります。
この状態では各論理ゲートや仮想処理がうまく結果を出力してくれません。

このショートが起こっているかどうか?だけを検出するのに少しのあいだ悩みましたが、以下の回路で検出できます。

片方に1が入力されたORゲートはnil(入力なし)や、0含む何かの入力がある場合は常に1を出力しますが、ショート入力の場合のみnil出力になります。
なので、nilをNOTゲートで1に変換してあげてショート検出器ができます。

このゲームの回路はnil・0・ショート状態をハンドリングしないといけないので最初はなかなか慣れませんでした。
気を抜くと思わぬところがnil出力になってしまったり…

Pulse Extender

入力がオンになると即座に出力もオンになり、以降入力がX秒間オフにならないと出力オフにならない回路。
1瞬のパルスもX秒間確実に続くよう伸長するのでPulse Extender…と一般的には呼ばれるらしいです。

Necesseでも作ったりしてましたが、shapezでも欲しくなり作ってみました。
これが意外と難しかったのですが、最終的にはベルトリーダーの出力を使うことにしました。

今のところ、非常に短い時間のパルスそれ単体はベルトリーダーにより2秒くらいまでしか引き伸ばせません。
それが動画の左側下レーンです。パルスが入力されるとフィルターが一瞬ゴミ箱方向へ向いてレーン上の物が動くのでリーダー出力が1になります。
レーン上の物はすぐにまた詰まって止まりますが、リーダー出力がふたたび0になるまでに2秒程度のラグがあるため、出力は2秒程度に伸長されたパルスになります。

それ以上長い出力を得たい場合は、上記の2秒パルスを使って、動画の左側上レーンのような"砂時計型"のPulse Extenderを作る必要があります。
仕組みは簡単で、基本的には一番左下と同じく、入力が0だと詰まり、1だとゴミ箱へ物体が流れるようなレーンを作るだけです。
違いとしてはベルトリーダーの位置で、末端から遠くすることにより「入力が0になって」から「物体が流入してベルトリーダーの地点に積み上がるまで」の間、ベルトリーダーの出力が1になり続けます。
流入速度を遅くすれば出力時間を伸ばせます。

非常に長いパルスを得たい場合

ただし、流入速度を非常に小さくして非常に長い時間出力を1にするのはできません。
あまりにも流入速度が小さいと物体が通っていない判定になりベルトリーダーの出力が0になってしまいます。
おおまかにですが、16/sのベルトのとき流速が2/s未満でベルトリーダー出力が0になるのを確認しています。

"出力が再びオフになるタイマーのフラッシュ"を担っている、物体とゴミ箱もPulse Extenderの制限になっています。
入力がオンになった時にレーンが確実に空になるのは、入力されるパルスの長さxベルトリーダー速度分のレーンだけです。
たとえば最初の2秒パルスを使い、レーン速度が16/sならば、そのパルスの間には32マス分の物体しか捨てられません。
32マス以上物体があっても捨てきれず残ってしまうため、パルスを伸長できる時間はレーンの長さで期待されるより短くなります。

これらの制限を越えて非常に長い時間伸ばすのであれば、2秒パルスPEを使って次のPEのゴミ箱を制御したように、PEの出力をまた別のPEのゴミ箱制御に流します。
たとえば2秒パルスを使ったPE1がレーン16/s、流入2/s、レーン32マスの場合、16秒パルスが持続します。
これを別のPEの制御に使うと、1パルスで捨てられる物体が16x16=256になるのでレーンを256マスまで伸ばすことができ、流入速度2/sで128秒持続するPEが作れます。

(それほど長い出力もいらないと思うので論理的お遊びの範疇ですが…)

ブレッドボード的な接続

回路作成のコツ

まっすぐなワイヤーはとりあえずWire Crossing付けておく

ワイヤー2色しかなくてめちゃくちゃ繋がりまくるなんやねんこれ、ってのがshapez回路の最初の印象だと思います。
それでもなんとか省スペースで回路組むためには見出しの通り、ここは曲がらず分岐せずまっすぐ進むだけ!というワイヤーをとりあえずWire Crossingで覆っておくのはいいと思います。

すごく簡単な例として、

2入力から下にそれぞれ線を引き出したいけど繋がってしまう…という時。
こんな時、左の入力から線はまっすぐ下に伸びているだけなので、これを全部Wire Crossingで覆います。


するとこんな感じで解決。
線にマウスオーバーするとどこと繋がっているのかわかります。
この時WCもハイライトされますが、横同士・縦同士しか繋がっていないので問題ありません。
たとえば1入力の方のワイヤーがWCに入っていますが、何もない左側に抜けているので縦方向の0入力と混線していません。
(それにしても不要なところまで繋がっているように見えて紛らわしいですが…)

WCの欠点は繋がっていないところまで繋がっているように見えることと、本当に不要な場所を繋いでしまうことがあること。
例えば以下の回路で…

緑にWCを被せると1マス挟んで隔たっていたはずの青線が接続してしまいます。

青線もWCで覆えればいいですが、別の回路と結線している部分があるとそこにWCを被せられなかったり…
この場合は緑線と青線ではさまれているのでWCを使わないようにしたり、回路によってはやはり線色を見直す必要がある場合もあります。

WC以外の接続回避

WCのことばかり書きましたが、信号はそのままに隣と接続してしまうのを回避するのに便利なものが2つほどあります。
1つはNOTゲート。入力信号が0/1か、出力を0/1に変換していい場合に使用が限られることと、信号を変更しないためには偶数個必要ですが、進行方向から横に出っ張らないのは便利なことが多いです。

もう1つはTransistor+定数1。
こちらは入力信号が0/1でなくてよく1つでも使えるすぐれもの。
ただし定数1を置くので1マス横に出っ張ります。

Transistorで逆流回避

他ゲームの回路システムでも非常に定番ですが…
Transistor+定数1は結線回避だけでなく、出力のフィードバックを防ぐ・線の色を変えるなどにも使えます。

例えば上の図で、右のスイッチ+赤はなにかの回路からの出力で、赤かnilのどちらかが来る、とします。
後ろへの入力がnilでは困るので、出力がnilだったら代わりに青を流したい、というのが上の青と、出力がnilかチェックするTransistorです。
ただ上の図の状態は定数青に結線されていないので未完成です。線を繋いで完成させてみると…

作ってみるとわかりますが、非常に小さな間隔で出力が青になったりnilになったりを繰り返します。
これは出力を青にしたことで、「出力がnilである」という青出力の条件が偽になり、青出力が止まりますが、そうなると今度は「出力がnilである」ので青が出力され…とループしてしまうからです。

これを解決するにはチェックする"古い出力"と、もしnilの場合に青を出力する"新しい出力"をTransistor+定数1で区切ります。

Transisterにより青出力はチェックしている出力の方に逆流していかないのでさっきのループは起こりません。

なお、右からの出力がnilでない場合はそれをそのまま左に流すという仕様も、Transistor+定数1は信号の内容を変更しないのでちゃんと実現できます。

出力シフト回路

順番を維持しながらnil入力のところを"上に詰めた"出力を出す回路を作りました。
以下では3入力3出力を作ったのでそれで説明しますが、入出力は増やせます。

※紛らわしい入力例を作ってしまったので、以降は図形のCu------をI、--Cu----をII、----Cu--をIII、------CuをIVと呼びます。

例えば以下のスクリーンショットの回路では、左側にある入力においてスイッチでI・II・IIIそれぞれの入力を流す流さないを決められます。

ここではスイッチにより入力がI・nil・IIIになっていますが、出力はnilの部分が"上に詰められ"、I・III・nilになっています
入力をスイッチで変更してnil・II・IIIとなるようにすると…

出力はII・III・nilとなります。
もちろんI・II・IIIと全て入力すれば出力はそのままI・II・IIIです。

回路図は以下のようになっています。

「詰められそうなら上の行に詰める」回路(以下上シフト回路)が3入力すべてに2回適用されている構成になっています。
今回は3入力3出力ですが、もちろん入出力は増やすことができます。が、上シフト回路が1度に1行しか移動しない都合上、入出力数-1分の上シフト回路が必要になる点だけ注意です。

仮想Stackerや仮想Painterはnil入力を積み重ねたり色を塗ったりしてくれずnil出力になるだけなので、図形がない部分や色を塗らない部分をnil入力にしていると思った出力が出ないことがあります。
(特に色は無色信号がありますが形は無がないのでStackerに無を入れられません)
それではnil入力になるのは入力の最後だけにすればよいのでは?…というのがこの回路を作った理由です。
結果的には実用はしませんでしたが何かに使えるかもしれません。

動作デモ:

図形の有無とStacker・Unstacker・Cutterを使って論理演算

shapezで0/1意外にも色や形の信号を送信できると知ったとき、0/1のデータを色や形に符号化できないか…?と考えました。
また色々考えてみると、Stackerによる積み重ねや、積み重ねたものが"下に落ちる"動作を使って図形操作でAND・OR演算ができそうだったので、実装してみました。

やりたいこと

図形操作で論理演算をするためには図形の有無を0/1として扱う必要があります。
また、1つの図形には4つのパーツがあるので、それぞれの有無で1つの図形は4bitの情報を持てることになります。
bitの並びの決め方は色々ありますが、今回は公式の記法の順番(右上→右下→左下→左上)で下位→上位bitとなるようにしました。
図形の形と色はなんでもいいのでCuを使います。
これらの取り決めにより、例えば1010を表す図形は--Cu--Cuとなります。

この図形表現を使ってAND・OR演算をする例としては、例えば以下のようになります。

左側上段と下段が入力でそれぞれ1010と0011。
右側の上段がその2入力のANDで0010、下段が2入力のORで1011になっています。
真ん中は2入力の図形をStackerで積み重ねたときの図形ですが、これをうまく処理すれば右側のAND・ORの図形になりそうですね。

ステップ1:bit表現の図形表現へのエンコード

とにかくやりたいことは4bitと4bitの演算なので、最初の入力は4bitのデータです。
ここでは4つのスイッチで4bitを入力するとして、それを図形表現に変換する回路は以下のようになります。

この図では上のスイッチから下位ビットの指定にしているため、入力は1010です。
右下に1010を表す--Cu--Cuが出てきているのがわかるかと思います。

考え方としては、各bitに対応するパーツをスイッチに応じて出す/出さないようにしてすべてのパーツを合成すればいいのですが…
ここでやっかいなのは何回か言っている"無を積み重ねられない"問題です。
仮想Stackerは2入力のうち1つでもnil入力ならばnil出力になります。
しかしここでは4つのパーツをすべて積み重ねたいので、nil出力ではなく存在する方の入力を出力にスルーして次のStackerに入るようにしなければいけません。

また、Stackerのどちらの入力が存在するかわからないと回路がややこしくなります。
よって今回はCwCwCwCwを"台紙"として使いそこに積み重ねていくことで、"常にStackerの下側入力は存在する"状態を作り、回路の複雑化を抑えています。

回路は全体として、先述したような入力のスルーができるStacker x4で台紙と1~4bit目パーツを合成する構成になっています。
また末尾に台紙を除去するUnstackerが入っています。
(入力が0000の場合台紙が出力にでてきてしまうのでTransistorを噛ませています)

動作デモ:



ステップ2:もう1つの図形表現との積み重ね図形作成

とにかく4bitを図形にできたので、別の4bit図形とのAND・OR演算をしてみたいと思います。
ステップ1の回路をもう1つ作って入力の2つ目となる4bitを作ってもいいのですが、ここではてっとり早く2つ目は定数信号で入れます。

そしてAND・OR演算のために、ステップ1の入力信号の図形と2つ目の図形の積み重ねを作ります。

回路右側は上から1つ目の図形、下が2つ目の図形定数信号です。
左側出力に積み重ね図形が出ます。
入出力のディスプレイ表示は以下のようになっており、問題なく積み重ねができています。

またここで無を積み重ねられない問題が発生しますが、必ずStackerの下側に入力が存在するようにするために、台紙ではなく"仮想Stackerの上側にしか入力がないとき、それを下側に回す"回路を使っています。
あとは前述のものと同じように、入力を出力にスルーできるよう回路を組んでいます。
これにより片方が無(0000)の場合でも問題なく存在する方の入力が出力にでてきます。

(台紙を使わないのは、Unstackerが上から層を剥がす関係上、積み重なりが発生する今回のケースでは台紙だけを剥がすことができないためです。
また、"下側に回す"回路は前述の4パーツ合成で使うと各Stackerに必要になるため回路が大きくなってしまいます。
無を積み重ねられない問題への対処をどちらでやるかはケースバイケースかと思います)

ステップ3:積み重ね図形からAND・OR演算結果の取得

ここまでくればあと一息です。
積み重ねた図形からAND・OR演算結果を取得する回路は以下のようになります。


左下から先程の積み重ね図形の入力、8個のディスプレイに表示されているのが出力です。

8個の出力は、上側がAND演算結果、下側がOR演算結果になっています。また、右側が下位bitになります。
このままだと出力が図形なので、厳密にbitにデコードしたい場合はNOT回路を2回挟めばOKです。

ちゃんと上は1010 & 0011 = 0010、下は1010 | 0011 = 1011になっていますね!

図形が出力されるのは意外と便利で、実はこのままステップ1と同じパーツの合成回路に入れればすぐに図形表現が得られます。
例えば以下の図はOR側の出力をそのままパーツ合成回路に入れている様子です。

OR出力は右が下位bit、パーツ合成回路は上から下位bitなのが注意点ですね。
右下にちゃんとOR結果1011の図形表現としてCuCu--Cuが得られているのがわかると思います。

まとめ

ということで、4bitデータを図形表現にしてStacker等の図形操作機械でAND・OR演算ができました。
ちなみに今回はやりませんでしたが、回転で循環シフト演算もできますね。

ちなみにこれをやって何か意味があるかというと…
ないかもしれないですが、最後ANDとORがUnstackerだけでスパッと出てくるところとかなんか見てて気持ちよさがないでしょうか…?

あと図形表現にしてデータを運ぶと0/1で運ぶより多くのデータを1本のワイヤで運べます。
あまりそういうケースはないかもしれないですが、shapez内でデータの長距離輸送をしたくなった時に使えるかも…

ただそういったある種のデータ圧縮用途としては次の話の方が適しているかもしれません。

図形によるデータ圧縮伝送

3bit←→色のエンコード・デコード

shapezの色はu(ncolored)まで含めて8色あります。3bitですね。
じゃあ色で伝送すれば1つのワイヤーで3bitの情報を送れますね。
そのエンコード・デコード回路を作りましょう!!

色のbit表現について、c=010, r=110...と適当に決めてもいいんですけど
r・g・bを各bitに割り当てて対応する原色が1となるようにしたら綺麗ですよね。
たとえばc=011(g・bの混色)、p=101(r・bの混色)...という感じで。
しかもそうするとbit→色のエンコードはMixerで対応色を混ぜるだけで済む!回路くまなくていいヤッター!!

なお仮想Mixerはありません。

ということで以下では泣きながらちゃんと作ったエンコーダーを載せますが、実Mixerをエンコーダーにしてもいいと思います。
入力3bitに対応する色をレーンに流してMixerで出てきた色をリーダーで読み取ってあげればいいでしょう。

といっても3bitデコーダー回路※なんてググれば出てくる…と思ったんですが
真面目に作ると3入力x8出力の24ワイヤーがズラッと並んでしまう…ので、r/gの組み合わせが同じものはまとめることでほんのりコンパクトにしました。
(こういう例も出てきそうなものなのにググってもなかった…現実では作りにくいから?)


いや真理値表を作るほどのことでもないんですけどね。

※普通は8出力one-hotの方が本来のデータで、それをbit表現に"エンコード"するのでbit→one-hotは"デコード"なんですが、今回はbitを色に"エンコード"するので…ややこしい

動作デモ:

エンコーダーがあるのでデコーダーも。

上の定数信号になってる部分が入力、デコード結果のbitが3出力に出ます。
上記回路ではわかりやすいようにディスプレイが上位bitから順にr・g・bに点灯するようにしています。

動作デモ:

ということで、これで3bit情報を色にエンコードして1本のワイヤで伝送し受信元で元のbit情報をデコードできますね。
0/1なら3本ワイヤー使わないといけないところが1本で済み、伝送効率がいい!!

1つの図形は何bitまで運べるか

1つの図形は何bitまで運べるでしょうか。
さきに書いたように色が3bit、図形がCRWSの4つで2bitの合計5bit。
(図形なしは色や形の情報と独立して持てないので考えない)
4パーツそれぞれに持てるので1層20bit。
4層まで重ねられるので80bit。
なんと10byteも運べるんですね!すごい。
10byteの入力を受け取って図形化して1本のワイヤで送信し受信先でデコードする…回路は作りませんが、作ることはできそうだという感じがしますね。
幸いにも層を分けたり層ごとに検査する装置は揃ってるわけですし。

なお色でbitを表すほうが仮想Painterが無色を塗れるので図形化が楽です。


(ただし上記の話の通りにすると、色を塗る塗らないではなく3bit→色のエンコード回路が手前に挟まってそれを塗る構造になるのでそれはそれで回路は大きくなる)

bit表現に対応する図形のデコーダー

一般的なデコーダー回路と違いないので特筆すべきところはないんですが一応メモ。


なんてことはなくて、bit表現と対応する図形を決めたらbit表現を入力とxnor→andで完全一致するかチェック、完全一致したら1を出力→定数信号の図形が外に出ていくというだけです。
xnorは同値比較装置があるのでそれでいいんですが、せっかく0/1入力ですしスペース的にも置けたので置いた感じ。

bitに対応する図形案

ちなみにbitに何を対応させるかですが…
最初は数字を出そうかと思い、頑張って作ってました。

そのうちこれがいいんじゃないか、と思いついてやってみたのがこれ。


これが数値表現になるかは…まあプロならなるんじゃないでしょうか(?)

(ちなみにこのツイートの時点ではbitの図形表現を色のu/wにしてますね。情報量がパーツ有無と変わらないのにAND・OR演算ができないのでボツ)

5bitでLv26まで表示できるとさらにいい感じかもしれないですね。
5bitだと4パーツすべてが同じ形・色の図形で表現できる範囲と同じなのでそれを変換してみたりとか…
27-32がないのでDon't care項も出そうですし、デコーダもガチガチじゃなくて少し最適化できるかな…等。

Pulse Extender考

(最初Pulse Extenderのところに書こうかと思ったんですがだんだんshapez関係なくなってきたので切り出し)

Necesseでも作ったPE、トラップとか監視装置のようなものを構築してると結構欲しくなるんですよね。
センサーとかの感度が良すぎてキャラがギリギリのところにいるとオンオフを繰り返すのを安定させたりとか、shapezでは実物を流して計算する回路で以前の部品が全部流れて結果が安定するまでに時間がかかるのでその計測に使いたくて作りました。

そんなPEですが意外とゲームの回路システムにはそのものずばりというものはなかったりします(なので作っているわけなのですが)。
一般的な電子回路をググってもまあコンデンサーそのものとかゲームの回路にはまずないので…なんとか他の部品の仕様を使って作ります。

結構ヒントになったのはMinecraftの、コンパレーターをループさせて信号減衰を利用するPEですね。
信号減衰なので入力がオンになると信号強度がMAXに、オフになったら信号強度が下がり始め、完全に下がったら出力がオフになる、ということでPEの要件を満たします。
この、「入力がオンになると即座にカウントがリセット」「入力がオフの間カウントダウン」「0になったら出力オフに」というセットを意識するとPEが作りやすい気がします。
(というか、この要素がコンデンサーの模倣ですね)

皆さんもいろんなゲームの回路システムでPE作ってみると楽しいかもしれません。

おわりに

ちなみにMAMまだ作ってません。