原初
原初、混沌があった。
混沌は凝り固まり…うんこになった!
世界は💩に覆われようとしていた。
その時!🚽が現れた。
💩の世界に現れた🚽は、次の規則に縛られていた。
-
誕生
💩に隣接する🚽が3つあれば、次の世代🚽が誕生する。 -
生存
-
🚽に隣接する🚽が2つか3つならば、次の世代でも生存する。
-
過疎
🚽に隣接する🚽が1つ以下ならば、💩にまみれて死滅する。 -
過密
🚽に隣接する🚽が4つ以上ならば、過剰により死滅する。
そう!たんなるライフゲーム!
Delphi
一昨年は、Windows, macOS 向けアプリを、昨年は、iOS, Android 向けアプリを作ったので、今年は!CUI のアプリケーションも作れちゃうんだ是!ってところを見せつけたいと思います。
では、ご覧ください!
Windows
macOS
Linux (Ubuntu 18.04)
Qiita に載せるためフレームレートを落としたのでガックガクですが、本当はもっと綺麗に動きます(いつも言う)
中でも macOS がガックガクなのは僕のスキル不足です(スクリーンキャプチャした mov ファイルから アニメーション gif にする最適解が解らなかった)
苦労したところ
プログラムは一瞬で組めたけど、作業時間の95割は各コンソールで💩の絵文字を表示するところ!
Windows
cmd.exe ではかなり頑張ったものの絵文字が表示できなかったので、Windows Terminal + PowerShell にして chcp 65001
したらあっさり表示された!
macOS
君は優秀!何もしなくて絵文字出た!
でも zsh はエスケープシーケンスが特殊なのでイーブン!
Linux
WSL でインストールされる Ubuntu を普通に動かすと cmd.exe ベースで動くので絵文字が出ない!
Windows Terminal を使うと無事絵文字が出た
…Windows Terminal が優秀なのでは??
ソース
短いので全文載せちゃう
program LifeGameConsole;
uses
System.SysUtils;
const
// 盤面の大きさ
N = 32 - 1;
// 生死の文字
STATE: array [Boolean] of String = ('💩', '🚽');
type
// 盤面の状態を表す2次元配列
TBoard = array [0.. N, 0.. N] of Boolean;
// 円環型座標取得
function GetPos(const iPos: Integer): Integer;
begin
Result := iPos;
if Result < 0 then
Result := N;
if Result > N then
Result := 0;
end;
// 全画面消去
procedure Clear; inline;
begin
{$IFDEF OSX}
Writeln('%{'#$1b'[2J%}');
{$ELSE}
Writeln(#$1b'c');
{$ENDIF}
end;
// 原点移動
procedure MoveOrg; inline;
begin
{$IFDEF OSX}
Writeln('%{'#$1b'[0;0H%}');
{$ELSE}
Writeln(#$1b'[0;0H');
{$ENDIF}
end;
begin
var Gen := 0; // 世代
var Board: TBoard; // 盤面
var Next: TBoard; // 次世代の盤面
// ランダムで初期化
Randomize;
for var Y := 0 to N do
for var X := 0 to N do
Board[X, Y] := Random(2) > 0;
// 画面クリア
Clear;
// メイン: 終了は Ctrl + C
while True do
begin
// 原点に移動
MoveOrg;
// 世代表示
Writeln(('Gen: ' + Gen.ToString + StringOfChar(' ', N)).Substring(0, N));
Inc(Gen);
// 盤面の表示と次世代判断
for var Y := 0 to N do
begin
for var X := 0 to N do
begin
// 盤面表示
Write(STATE[Board[X, Y]]);
// 次世代の生死判断
var C := 0;
for var Q := Y - 1 to Y + 1 do
begin
var V := GetPos(Q);
for var P := X - 1 to X + 1 do
Inc(C, Ord(Board[GetPos(P), V]));
end;
Next[X, Y] := (C = 3) or ((C = 4) and Board[X, Y]);
end;
Writeln;
end;
// 世代交代
Board := Next;
Sleep(16);
end;
end.
個人的に気に入っているのは次世代の生死判断のところです!
よろしくお願いいたします!
さいごに
クソアプリアドベントカレンダーなのに、ゲームを作ってしまった!!!