先日qiitaを散歩していると、短い行数で且つそれなりの可読性があるライフゲームを作るという記事を見かけたました。ちょっと古い記事ですが。
【C#】26行でライフゲームを書いてみる(コンソール表示可能) - Qiita
本記事は C# その2 Advent Calender2018 17日目の記事です。はじめにこんにちは@yoship1639です。普段は個人ゲーム開発を行っています。本記事は内容は技術的です…
これ、LINQ使ったらもうちょっといけるんじゃね?ということでやってみました。
れぎゅれーしょん
- ライフゲーム
- C#
- それなりの可読性
- 縦横ともに一画面になんとなく収まるぐらい
- エラーハンドリングは考えない
ソース
class Program { const int kWidth = 10; const int kHeight = 10; public static void Main(string[] args) { var cells = (new bool[kHeight + 2]).Select(_ => (new bool[kWidth + 2]).ToList()).ToList(); var accessor = new []{(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)}; int CountOf(int x, int y) => accessor.Aggregate(0, (l, set) => l + (cells[set.Item1 + y][set.Item2 + x] ? 1 : 0)); bool IsAlive(bool alive, int count) => (!alive && count == 3) || (alive && (count == 2 || count == 3)); string[] sc = null; while ((sc = System.Console.ReadLine().Split(' '))[0] != "q") { if (sc.Length == 2) { cells[int.Parse(sc[1]) + 1][int.Parse(sc[0]) + 1] = true; } else { cells = cells.Select((line, y) > y == 0 || y == kHeight + 1 ? line : line.Select((alive, x) > x == 0 || x == kWidth + 1 ? false : IsAlive(alive, CountOf(x, y))).ToList()).ToList(); System.Console.WriteLine(string.Join('\n', cells.Skip(1).Take(kHeight).Select(y => string.Join("", y.Skip(1).Take(kWidth).Select(x => x ? '■' : '□'))))); } } } }
概要
標準入力からとってくるところは元記事をそのまま使っています。エラーハンドリング、異常値チェックなども無しで踏襲しています。
とりあえず二次元配列には消えてもらう
二次元配列はLINQが微妙なので、ListのListにしています。
とりあえず番兵
まあ、あった方がいいよね。
周囲を数えるのはインデックスを用意
Aggregateで畳込めるしインデックスの配列を用意しています。Enumerable.Rangeで3×3のEnumerableを用意してもよかったかもしれません。
入力周りは元のまま
元のまま使わせてもらっています。
次世代へはLINQで1式で
番兵以外は周囲の生存数を数えて、生き死に判定して新しいリストを再代入しています。
SelectではなくForEachにして、直接Listの中を書き換えると番兵外しの三項条件演算子がSkip().Take()に変えられると思います。そっちの方がわかりやすかったかも。
まとめ
もっと詰め込みに詰め込めばまだまだ減らせそうですが、人間にはちょっと前衛的すぎることになりそうですので、個人的にはこの辺がギリギリラインではと勝手に思っています。