はじめに
QiitaのMastodonサービスQiitadonが500文字までの文章を入力できてコードブロック&マークダウンに対応してると聞いたもので、喜び勇んで勉強中のKotlinでTicTacToe(3目並べ)を作ったは良いが500文字に収めるのに挫折したのが8年前、ちがった8日前でした。

この投稿の後、同様にSmalltalkで3目並べを500文字プログラミングしようとして挫折した話(Smalltalkで三目並べを500文字プログラミングしようとして挫折した)や、500文字以下に抑えられたJavaScript版のテトリスの話(452バイトテトリス)を伺って、断然やる気がみなぎってきたので、時間を捻出してようやくプログラミングしたコードですが、みなぎったやる気と相関しない技術力のせいで500文字には収まらず、コメント外しても1000文字オーバー。やる気ってなに?
という感じで、500文字以内に収めることに挫折したので、コードをさらして次の糧にしたいと思います。文字数を抑えるため、可読性が落ちていますがご容赦を。
例によってKotlinいいね!って処をコードの後のほうにまとめています。
コード
import java.lang.Thread.sleep
fun main(args: Array<String>) {
    val W = 10;//盤面の横幅
    val H = 10;//盤面の縦幅
    var d = arrayOf(//盤面データ。生きているセルを"o"でマーク。ここ書き換えてください。横1-8、縦1-8までが有効な盤面
            "+--------+".toCharArray(), //1
            "|  o  o  |".toCharArray(), //2
            "|  o  o  |".toCharArray(), //3
            "|oo oo oo|".toCharArray(), //4
            "|  o  o  |".toCharArray(), //5
            "|  o  o  |".toCharArray(), //6
            "|oo oo oo|".toCharArray(), //7
            "|  o  o  |".toCharArray(), //8
            "|  o  o  |".toCharArray(), //9
            "+--------+".toCharArray())//10
    while (true) {//生命には終わりがないため、無限ループ
        var v = Array(W, { IntArray(H) })//周辺環境計算用盤面データ
        for (i in d) println(i)//盤面表示
        println()
        fun recr(x: Int, y: Int) {//盤面判定用の再帰関数
            if (d[y][x].equals('o'))
                for (i in -1..1) for (j in -1..1) if (i or j != 0) v[y + i][x + j]++ //周辺セル情報の計算
            if (x < W - 2) recr(x + 1, y)//右のセルに移動して再帰的に判定
            else if (y < H - 2) recr(1, y + 1)//直下左端のセルに移動して再帰的に判定
            d[y][x] = if (v[y][x] == 3 || (v[y][x] == 2 && d[y][x].equals('o'))) 'o' else ' '//周辺情報をもとにセルの生死判定
        }
        recr(1, 1)
        sleep(1000)
    }
}
解説
タイトルに記載の通りライフゲームを実装しました。ライフゲームは単純なルールで世代交代させているだけなのに何故か生命の息遣いを感じるようなそんな環境ゲーム。スタートしたら、基本見てるだけです。
ライフゲームの解説はWikipediaに詳しいのでそちらをご参照ください。(ライフゲーム - Wikipedia)
実行例
+--------+
|  o  o  |
|  o  o  |
|oo oo oo|
|  o  o  |
|  o  o  |
|oo oo oo|
|  o  o  |
|  o  o  |
+--------+
+--------+
|        |
|  o  o  |
| o oo o |
|  o  o  |
|  o  o  |
| o oo o |
|  o  o  |
|        |
+--------+
+--------+
|        |
|  oooo  |
| o oo o |
| oo  oo |
| oo  oo |
| o oo o |
|  oooo  |
|        |
+--------+
+--------+
|   oo   |
|  o  o  |
| o    o |
|o      o|
|o      o|
| o    o |
|  o  o  |
|   oo   |
+--------+
+--------+
|   oo   |
|  oooo  |
| o    o |
|oo    oo|
|oo    oo|
| o    o |
|  oooo  |
|   oo   |
+--------+
+--------+
|  o  o  |
|  o  o  |
|oo oo oo|
|  o  o  |
|  o  o  |
|oo oo oo|
|  o  o  |
|  o  o  |
+--------+
+--------+
|        |
|  o  o  |
| o oo o |
|  o  o  |
|  o  o  |
| o oo o |
|  o  o  |
|        |
+--------+
Kotlinいいね!
今回のコードでKotlinっていいね!と感じた処をまとめました。
ローカル関数
Kotlinはローカル関数をサポートしていて、関数内に関数を定義することができます。
今回は、盤面のセル判定をローカル関数で再帰的に実施し、コール前に周囲環境の判定をコール後にセルの生死判定を行っています。盤面の情報はローカル関数の外から取得しています。楽ですね。
        fun recr(x: Int, y: Int) {//盤面判定用の再帰関数
            if (d[y][x].equals('o'))
                for (i in -1..1) for (j in -1..1) if (i or j != 0) v[y + i][x + j]++ //周辺セル情報の計算
            if (x < W - 2) recr(x + 1, y)//右のセルに移動して再帰的に判定
            else if (y < H - 2) recr(1, y + 1)//直下左端のセルに移動して再帰的に判定
            d[y][x] = if (v[y][x] == 3 || (v[y][x] == 2 && d[y][x].equals('o'))) 'o' else ' '//周辺情報をもとにセルの生死判定
        }
        recr(1, 1)
if文は式
Kotlinのif文は式です。なので、結果を変数に代入できます。
今回は、盤面のセルの生死判定をif文で判定し、結果を盤面情報に代入しています。
            d[y][x] = if (v[y][x] == 3 || (v[y][x] == 2 && d[y][x].equals('o'))) 'o' else ' '//周辺情報をもとにセルの生死判定
おわりに
まだまだ、Kotlinの良いところを引き出せていませんね。
俺たちの戦いは始まったばかりだ!
enu7先生の次回作にご期待ください。
ではまた!
追記
・@shiracamusさんがコード最適化して500文字以下版を作ってくださいました。感謝!(コメント欄見てね)
・@sumimさんがSmalltalkで500文字以下で実装してQiitaのMastodonサービスQiitadonに投稿されました。期待してました!

