イクチオカップ

アンミカリバーシ

https://github.com/2509-hackz-ichthyo/main

Go

AWS

Terraform

200色(くらい)の白を操り、盤を染め上げよう

yotu

rayfiyo

推しアイデア

半年間温めた点

作った背景

おもろいから

推し技術

すべて Golang で開発(すごい!) Whitespace を使用している(白なので)

プロジェクト詳細

はじめに

白を 200 色使って遊ぶリバーシです。 (アンミカさんに)怒られたら消えます。

アプリ概要

ゲーム部分

デプロイ先 にアクセスすると、ゲームのマッチング処理が自動で開始されます。 過疎ゲーなので、マッチングしない場合は二枚ページを開くなりして自分とマッチングすることをおススメしています。 image

マッチングが成立すると、自動でゲーム画面へと遷移します。 image

ここからは基本的にリバーシと同じルールです。 しかし、明確に異なるのが...

  • 任意のコマと挟めるコマが1つでもあればコマが置ける
  • 挟まれたコマは、挟んだ2つのコマの色を足して3で割った色になる image

つまり、相手のコマをはさんで自分の色に寄せたり、逆に自分のコマをより白/黒くすることもできます(強くはありませんが.......)。

対局が進むと、こんな感じでグレーな盤面が広がります。残念ながらこのゲームでは 64 色しか盤面に存在できませんが、200 色の白があることを実感できますね。

image

最後までプレイすると、勝敗判定が行われます。 このリバーシは特殊で、盤面のコマの色をすべて足して平均した値が、どちらの色(白/黒)に近いか で勝敗を判定します。 無事に勝利できると、以下のようになんともいえない祝福をされます。おめでとう。

image

24/7

アンミカリバーシを今後 eSports 界のトップストリームにするため、リプレイを 24 時間流し続けるページを用意してみました。

過去の対戦履歴がアーカイブに残っており、専用のプレイヤーによって延々と流れます。

技術

All Golang + AWS 構成となっています。

image

game

Ebitengine で実装したゲームアプリを WebAssembly でビルドし、AWS S3 + CloudFlont でホスティングした静的サイトから呼び出しています。

24-7

当初は MediaLive を使用しようとしていました...が、1日当たりのコストが4,000円(概算)と断念。こんなサービスのために金欠にはなりたくありません。 代わりに wasm で作成したリプレイプレーヤーを実装し、そこにアーカイブした対局データを流し込むことで実装しました。 ちなみに対局データは Whitespace 形式で保存されています(後述)。

api

Gin で作成した Web API サーバをコンテナとして ECR にデプロイし、ECS でホスティングしています。 ちなみに API サーバは主に Whitespace のインタプリタとして動いています。これがコスト的に一番幅を利かせてるのおもろい。

realtime

ゲームのマッチングや処理に使用するリアルタイム通信部分は、API Gateway WebSocket + Lambda + DynamoDB Streams で実現しています。 当初は Gin に gorilla でリアルタイム通信を生やして実装する予定でしたが、通信の到達性(相手プレイヤーに自分の通信が届いたか)が保証できないため断念。この点 API Gateway WebSocket はコネクションが確立しているときのみ通信を行うため、この課題を解決しています。

ちなみに、ちゃんと lambda も Go で書いています。あまり一般的ではないので資料が乏しく、実行までに苦労しました。 実際に使用する際は 公式記事 をチェックすることをおススメします。とくに実行環境周りは既存の記事だと情報が古い可能性があります。

other

ここまでのリソースをすべて Terraform で IaC として定義しています。各リソースの繋ぎ込みがかなり楽になりました。

技術の無駄遣いポイント

Whitespace

Whitespace は、スペース・タブ・改行だけを用いたジョーク的プログラミング言語で、外見上は「何も書かれていない」プログラムになるのが特徴です。また、一通りの命令セットと、チューリング完全より「実用的に動く」ことも保証されています。

このサービスでは対局データの保存に使用しています。 まず、見にくい(というか見えない) Whitespace を使わずに説明します。対局データは、1つの駒に対して、

{row_1} {column_1} {color_1}

という形式の文字列を取ります。これを駒の数だけ要素を持つ配列で保持しています。 制約として、row_1column_1 は 0 ~ 8 の整数値、color_1 は 0 ~ 255 の整数値です。また、配列は最小で 0 個、最大で 64 - 4 = 60 個です。

例えば、12 行 4 列 に 125 番目に白い色の駒を置くことは次のように表現されます。

11 3 125

そして実際は、これを Whitespace で表現した

に変換してDBに保持しています。 一目瞭然ですが、3 つの数字をそれぞれに push 命令を使って表現しています。

さらに、前述の通り、打った駒の数だけ配列の要素を持つので次のようになります。

image

この Whitespace と数値の変換(エンコード・デコード)は 自作のインタプリタ を用いています。 これは、Go言語で作成され、クリーンアーキテクチャを用いています。 また、その特性を活かし、テストコードの全体に対するステートメントカバレッジは 100.0% を達成しています。(あまりにも過剰)

なお、この実装によって、データが効率的に圧縮され、通信量が節約される……というようなことはなく(むしろ増えており)、サーバー代と人的リソースを効率的に浪費することに成功しています。

おまけ

アイデアについて

このアイデアは半年前からアイデア帳にあったんですが、ハッカソンのたびに却下され続けていました(なんで?)。 今回も例に違わず却下されていましたが、メインシステムを作成する Unity 班が病に倒れてしまい、元プロジェクトが続行不可能になったので、仕方な~~~~~~~~くこのアイデアを出しました。 嬉しいくせに

白黒つかないバグ

開発中、コマの色がクライアントによって変わるバグがあり、白黒つかないことがありました。これはこれで面白い。

image

GPT-5 と Whitespace

ChatGPT は LLM であって、インタプリタではございません。 実際に、

このコードを実行結果を要求すると、1m 29s 思考の末に次を返します。

image

「見た目は何もないように見えますが、内容は入っています」

やかましい…

イクチオ

PJ 名にいれましたが、typo を 500 回くらいしました。ichthyo です。覚えよう。

image

念の為 ichthyo を標準出力する Whitespace を置いておきます。

yotu

@yotu