「ドゥーム(Doome)」というゲームが、カーリング(Curling)の盤上でプレイされている様子。

2026/04/12 19:01

「ドゥーム(Doome)」というゲームが、カーリング(Curling)の盤上でプレイされている様子。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

本書では、標準的なコンピュータターミナルウィンドウ内においてクラシックゲーム「DOOM」をストリーミング再生する方法について説明しています。この方法により、重厚なグラフィックドライバや外部クライアントの使用を不要とします。単一の TCP 接続を通じて入力を受け取り、ビデオフレームを送信することを同時に実現することで、

cURL
bash
のようなツールを用いた単純な HTTP リクエストによって双方向のゲームプレイが可能になります。この技術的画期的進歩により、コマンドラインインターフェース内だけで商業用ゲームを体験できるようになり、ハードウェア要件を大幅に低減させるとともに特定のオペレーティングシステムへの依存性を排除します。バックエンドを構築するには現代の Node.js 環境と具体的なソースファイルが必要ですが、一旦実行を開始すれば、プレイヤーはテキストコマンドを通じて W A S D の移動操作やレベル間の変換を行うことができます。
cURL
で管理されるセッションを終了させる際にはターミナルの状態を手動でリセットして通常の動作を復元する必要がある場合もありますが、この軽量なソリューションはリソースが限られた利用者にも複雑なゲームを追加ソフトウェアやドライバをインストールせずにプレイできる手段を提供します。

Text to translate:

Summary: The text describes a method to stream the classic game "DOOM" directly inside a standard computer terminal window, eliminating the need for heavy graphics drivers or external clients. By using a single TCP connection to simultaneously handle incoming input and outgoing video frames, this setup achieves bi-directional gameplay via simple HTTP requests with tools like

cURL
and
bash
. This technical breakthrough allows users to experience commercial gaming entirely within a command-line interface, drastically lowering hardware requirements and removing dependencies on specific operating systems. While building the backend requires a modern Node.js environment and specific source files, once running, players can control WASD movement and warp between levels directly through text commands. Although quitting a session managed purely by
cURL
may require manually resetting the terminal state to restore normal behavior, this lightweight solution offers an accessible way for those with limited resources to play complex games without installing additional software or drivers.

本文

cURL DOOM: HTTP プロトコルでプレイする DOOM

curl
を介してブラウザやターミナルから遊べる DOOM。HTTP サーバーが DOOM のフレームを ANSI の半ブロック文字に変換し、それを
cURL
を使ってターミナルへストリーミング送信します。

  • インストール不要
  • 依存要件は
    curl
    bash
    のみ

遊び方 2 つ

1. 親しみやすい方法:
curl | bash

curl -sL http://localhost:3000 | bash

仕組みはどうなっているのでしょうか? GET

/
リクエストはコンテンツネゴシエーションによって処理されます。
curl
は取得元のホスト名を
__SERVER__
変数として置き換えた
play.sh
スクリプトを受け取ります。このスクリプトが、キー入力を逐次処理する
/tick
ループを実行し、
stty
の設定、代替画面モード、カーソル操作、そしてクリーンアップを処理します。同じ URL にアクセスしたブラウザは、ワンライナーを表示するだけの小さなランディングページを受け取ります。

2. クソツメな方法:純粋な
curl
とシェルループなし

stty -echo -icanon min 1 time 0 && curl -sN -X POST -T - localhost:3000/play
  • デフォルトでは小さな画面サイズです。設定方法は後述します。

プレイ開始は、何らかのキーを押して。

  • 終了するには
    Ctrl+C
    を押してください(※ `'q' キーは無効です)。
  • ターミナルを正常な状態に戻すには、完了後に
    reset
    コマンドを実行してください。

デフォルトの小さな画面ではなく、列数と行数を変更したい場合は、以下のように入力します:

curl -sN -X POST -T - "localhost:3000/play?cols=200&rows=60"

仕組みはどうなっているのでしょうか? 1 つのストリーミング HTTP リクエストで、双方向通信を行います。入力キーはリクエスト本文(ボディ)を介して送信され、ANSI フレームはレスポンス本文を介して返送されます。キー入力を逐次処理するラッパーや、キー入力ごとに往復するオーバーヘッドがありません。これは単一の TCP 接続で、送信側と受信側の両方を同時に行うものです。

ただし注意点として、シェルは通常ターミナルを「熟練モード(cooked mode)」に設定しており、これにより (a)

stdin
がバッファリングされ Enter キーを押すまで
curl
はキー入力を検知できず、(b) ターミナルに入力した文字がそのままフレームの上に重畳して表示されるという問題が発生します。そのため、まずターミナルを生モード(raw mode)に切り替える必要があり、作業終了後にも元に戻す必要があります。したがって、
curl
の前に
stty
コマンドを実行し、完了後に
reset
を呼び出す必要があるのです。

よりクリーンに、少し長い手順で行うこともできます:

( stty -echo -icanon min 1 time 0 < /dev/tty
  trap 'stty sane < /dev/tty' EXIT INT TERM
  curl -sN -X POST -T - localhost:3000/play < /dev/tty )

(行数と列数の設定方法については、上記をご参照ください。)

キーの押したまま操作する場合: サーバーは各キー入力を受信した後、最後のバイトを 150 ms 経過後に解放します。したがって

w
キーを押したままにすると、滑らかに前方に進むことができます。終了するには
Ctrl+C
で切断してください。トラップ処理により、いずれの場合でもターミナルは正常な状態に戻ります。


スムーズさについて

/play
エンドポイントはデフォルトで FPS 15 に設定されています。これは、
-T -
オプションを使用した
curl
が、
stdin
に入力がない間(
read(stdin)
でブロックされている間)レスポンスソケットを処理しないため、キー入力があるまでフレームがカーネルの送信バッファに溜まってしまうからです。何かを押すと一度に大量のデータ(バースト)が排出されます。15 FPS は、ターミナルが次のフレームが到着する前に各フレームをレンダリングできる程度に、そのバーストを小さく抑えるために設定されています。

上書きする方法:

curl ... "http://localhost:3000/play?cols=200&rows=60&fps=25" …

各フレームはカーソルを画面先頭に移動して元の位置で塗りつぶす方式(各フレームごとにスクリーン全体をクリアしない)なので、遅いターミナルが追いつけなくても、最悪の場合「引き裂かれた」フレーム(上部が N+1 フレーム、下部が N フレーム)しか見られず、白画面にはなりません。

もしプレイせずに見るだけ(入力に関係なくフレームがストリーミングされる)場合は、

stty
の設定も不要で、
-T -
によるブロックも発生しないため、デフォルトの 15 FPS は十分滑らかで、より高画質にすることも可能です:

curl -sN -X POST "http://localhost:3000/play?cols=200&rows=60&fps=30"

DOOM が自分自身で動き出します。飽きたら

Ctrl+C
で終了してください。


実装概要

ターミナル                             cURL DOOM サーバー
-------------                        ----------------
curl GET /          ---------->      play.sh
                          <----------   (__SERVER__ を書き換えたもの)
bash の pipe に接続

stty raw モード
キー入力を読み取る
curl POST /tick?s=&key=  -------->   doom セッションにキー情報を送り込み
                            <--------   doom のフレームバッファから ANSI フレームを受信
/dev/tty に出力
ループ処理を継続

サーバーは各セッションにつき、1 つの

doomgeneric
プロセスを持続します。各セッションには以下のものが割り当てられます:

  • テキストコマンド(
    K
    : キー入力、
    T
    : アドバンス TIC、
    F
    : フレームダンプ、
    Q
    : 終了)をプッシュする標準入力パイプ、
  • fd 3 の専用フレームパイプ(stderr での printf ロギングがバイナリフレームバッファを汚染しないよう確保)、
  • ヘッドレスなバックエンドが
    DG_SleepMs
    の内で増やされる仮想的なクロック(doom の「次の TIC まで待つ」ループが即座にブロック解除されるように)。

doom から出力される各フレームは、640×400 ピクセルの BGRA 形式(1 MB)です。サーバーはこのデータをターミナルの列数×行数×2 のピクセルグリッドにダウンスAMPLE します。その際に上半分ブロックのグリフ

を使用し、フォアグラウンドが上画素、バックグラウンドが下画素という方式により、無料で垂直解像度を実質 2 倍にしています。また、色が実際に変化する時だけ SGR エスケープシーケンスを送信するため、レスポンスサイズは約 5 分の 1 に圧縮されます。

アイドル中のセッションは 60 秒後に除去(リイプト)されます。Node プロセスを強制終了すると、子プロセスとして実行されているすべての doom も共に終了します。


サーバーのセットアップ

(これはゲーム自体を遊ぶためのものではありません。)

要件

  • Node.js 18 以上
  • C コンパイラ (
    cc
    /
    clang
    /
    gcc
    ) と
    make
  • doom1 シェアウェア WAD ファイル
  • doomgeneric のソースコード

ビルドと実行

# 1. Node の依存ライブラリをインストール
npm install

# 2. ヘッドレスな doom バイナリをビルド(一度だけ実行)
cd doomgeneric/doomgeneric && make -f Makefile.server && cd ../..

# 3. サーバーを起動
npm start
# -> cURL DOOM は http://localhost:3000 で動作開始
# -> プレイ方法:   curl -sL http://localhost:3000 | bash

コードは

doom1.wad
(自由に配布されているシェアウェアエピソード)を前提としています。他の WAD を使用する場合は、それをプロジェクトのルートディレクトリに配置し、
index.js
内の WAD 定数を編集してください。


コントロール

キー動作
W
/
前進
S
/
後退
A
/
左に回転
D
/
右に回転
,
/
.
左へ横移動 / 右へ横移動
F
攻撃 (発砲)
スペース /
E
アイテム使用 / ドア開閉
Tab
オートマップ
Enter
メニューの確定
Esc
メニュー / 戻る
Y
/
N
メニューダイアログでの「はい」/「いいえ」
Q
終了

セッションは Hurt me plenty エピソード(

-warp 1 1 -skill 3
)のエピソード 1 メイン 1 (E1M1) に即座に移行するため、タイトル画面やメニュー操作をスキップできます。


カスタマイズ

環境変数デフォルト効果
DOOM_SERVER
http://localhost:3000
クライアントが接続するサーバーURL
DOOM_COLS
ターミナル幅描画ビューポートの幅を強制設定
DOOM_ROWS
ターミナル高さ - 1描画ビューポートの高さを強制設定
PORT
3000
サーバー側:リスニングポート番号

クライアントは

stty size < /dev/tty
を使用してターミナルサイズを自動検出します(
ioctl(TIOCGWINSZ)
でカーネルの TTY 状態を読み取り、失敗した場合は
tput
$LINES/$COLUMNS
にフォールバック)。DOOM のネイティブ解像度(半ブロックグリフ下)は 320x200 ピクセル(= ターミナルセル 320列 x 100行)であり、それより大きいサイズでもクリッピングされます(アップスケーリングのみになるため)。

大きなターミナルで小さなビューポートを強制するには:

DOOM_COLS=120 DOOM_ROWS=40 ./doom.sh

リモートサーバーに接続したい場合は:

DOOM_SERVER=https://doom.example.com ./doom.sh

HTTP API

すべてのルートは

?cols=N&rows=N
パラメータを受け付け、描画ビューポートを上書きすることができます。

  • GET
    /
    : コンテンツネゴシエーションされたランディングページ(curl 用スクリプト、ブラウザ用 HTML)
  • POST
    /new
    : セッションを作成し、初号フレームを返送する。レスポンスヘッダー
    X-Session
    にセッション ID が含まれる
  • POST
    /tick?s=&key=
    : 1 キーの操作をプッシュし、約 5 TIC を進める。次回のフレームを返送する
  • POST
    /play?cols=&rows=&fps=
    : バイディレクショナルなストリーミング処理。リクエストボディにキー入力を、レスポンスボディに ANSI フレームを送信(デフォルト FPS 15、範囲は 5-35)
  • POST
    /quit?s=
    : セッションを即時に破棄する(60 秒待機なし)
  • GET
    /health
    :
    {"sessions": N}
    のようなセッション数を返す

クレジット

  • 作成者:Sawyer X.
  • DOOM: id Software, 1993
  • doomgeneric: ozkl 氏による、カスタムレンダリングバックエンドを切り替えられるための抽象化。
  • doom1.wad: 自由に配布可能なシェアウェアエピソード

コミット履歴

コミット履歴は短く、説明不足です。これは私が恥ずかしい C のミス、ひどい JavaScript、恥ずかしいタイプミスのため、履歴を書き換えたからです。

その作業には結構時間がかかりました...

なぜか? だって DOOM があるからです。

同じ日のほかのニュース

一覧に戻る →

2026/04/13 4:44

怠惰と失われた危機の罠

## Japanese Translation: テキストは、ラリー・ウォールのプログラミングにおける「怠け」、不満、そして大それた振る舞いの美徳が依然として不可欠であると論じつつ、特に未来の利用に最適化された単純かつ強力な抽象化を駆使する「怠け」においてはその重要性が高いと指摘しています。これに対して、大規模言語モデル(LLM)は危険に「徳ある怠け」を、「 Hustle Porn」と呼ばれる偽の勤勉で置き換えてしまいました。人間とは異なり、LLM は作業に対してコストを負担せず、時間制約も無視するため、自慢のためのメトリクスによってではなくエンジニアリングの厳密さによってではない、不要なコードの層を蓄積します。この転換は、「Camel Book」という歴史的智慧において、即時の出力量よりも使いやすさを重視する方針と対照付けられます。具体例として、LLM を用いて 1 日 37,000 行ものコードを書き上げたというブロプログラマの Garry Tan の自慢に対し、Gregorein の分析によって多数のテストハネスとゼロバイトのロゴが示されており、これは無制約な AI が肥大化したシステムを生成する様子を明らかにしています。もし無制約のままにされれば、LLM は「ゴミ」の層を積み重ねることで単純な設計ではなく技術的負債の形で未来の世代を脅かすことになります。しかし、業界の行方はエンジニアがこれらの AI モデルを徳ある怠けの方へ導けるかに依存しています。制約と Oxide ガイドラインに導かれ、LLM が複雑性を削減し、厳密な設計を促進して将来に向けてより単純かつ強力なソフトウェア抽象化をもたらすことを保証することが可能です。 ## Text to translate: The text argues that while Larry Wall's programming virtues of laziness, impatience, and hubris remain essential—especially laziness, which drives simple, powerful abstractions optimized for future use—Large Language Models (LLMs) have dangerously displaced "virtuous laziness" with a form of false industry called "hustle porn." Unlike humans, LLMs incur no cost for work and ignore time constraints, causing them to accumulate unnecessary code layers driven by vanity metrics rather than engineering rigor. The summary contrasts this shift with historical wisdom found in the "Camel Book," which prioritizes ease of use over immediate output volume. Examples include broprogrammer Garry Tan bragging about writing thirty-seven thousand lines of code per day with LLMs, while Gregorein's analysis revealed multiple test harnesses and a zero-byte logo, illustrating how unchecked AI generates bloated systems. If left unconstrained, LLMs will threaten future generations with technical debt by building up "garbage" layers rather than simpler designs. However, the industry's trajectory depends on whether engineers can steer these AI models back toward serving virtuous laziness; guided by constraints and Oxide guidelines, we can ensure LLMs reduce complexity and promote rigorous design to yield simpler and more powerful software abstractions for the future.

2026/04/12 21:21

慣用表現に基づくデザインを取り戻そう

## Japanese Translation: 現代の Web アプリケーションは、デスクトップ時代の統一的なインターフェース標準を捨てて、基本的なタスクを第一原理から再発明し、リアルタイムコラボレーションなどの機能に優先順位を置いている。この変化により、ユーザーはチェックボックスによるログイン永続化や標準的なクレジットカード入力フィールドといった馴染み深い「デザイン慣習」に頼るのではなく、新たな相互作用パターンを絶えず学ぶことを余儀なくされている。Figma や Linear などのツールは設計が良くても、共有アイコンやキーボードショートカットの欠如により断片的な体験をもたらしており、OS によって強制されたライブラリによる予測可能な動作を保証した Windows 時代の均質なインターフェースとは対照的である。この増大するユーザー摩擦に対処するためには、HTML と CSS で定義されるコアなブラウザ慣習に回帰すべきであり、開発者はあいまいなアイコンよりも一貫したアクション、信頼できるデフォルト値、明瞭なテキストラベルを優先することで、予測可能性を取り戻し、ユーザーの学習時間を短縮し、UI 標準の全面的な再発明なしにも Web エコシステムへの信頼を再構築できる。

2026/04/13 1:38

# DIY ソフトドリンクの作り方 「DIY ソフトドリンク」というタイトルは適切ですが、コンテンツをさらに充実させるための構成案をご提案します。 ご自身の手に入れた素材や好みの味に合わせて自由にアレンジしてみてください。 --- ## 【必要なもの】 * 炭酸水(またはレモンサワーベースのシロップ) * シュガーシロップ(または蜂蜜、アガベシロップなど) * お好みの果実(レモン、ライム、オレンジなど) * 氷 * ガラス瓶またはペットボトル(中性洗剤で十分に洗浄したものですこと) ## 【基本レシピ】 1. **シロップの調製** 砂糖と水(甘さを抑えたい場合は 2:1 の割合、通常は 1:1)を鍋に入れて、弱火で溶かします。 お好みの果実を加えて香りを出したら、冷まします。 2. **果汁の絞り方** 新鮮な果実をよく洗い、果汁を搾ります。 必要であれば、果肉も一緒に漉すなどして、澄んだ液体に仕上げてください。 3. **混ぜ合わせ** シロップと果汁をお好みの比率で混合します(例:シロップ 2 :果汁 1)。 グラスや容器に移し替えて、冷蔵庫で保存してください。 4. **炭酸水とのミックス** 氷を入れたグラスに、少量の自家製果汁シロップを入れてから、炭酸水を注ぎます。 スパチュラなどで軽く混ぜ合わせれば完成です。 ## 【ポイント】 * 甘さは控えめが基本ですが、お好みで砂糖の量を調整してください。 * 新鮮な果実を使うことで、より自然な香りを堪能できます。 * 保存期間は冷蔵庫に置いても 2〜3 日が目安です。

## Japanese Translation: 本稿は、糖分・カフェインフリーで正確な自家製コーラレシピを開発する革新的なプロジェクトを記録しています。2020 年以降、著者は Open Cola や Cube Cola などのブランドに着想を得て、オレンジ、ライム、レモン、ナツメグ、シナモン(クサヤ)、コリアンダー、ラベンダーといったエッセンシャルオイルとアラビアゴム(天然の乳化剤)を主成分とする配合を開発しました。計測には 1 ml のインシュリンペン用シリンジを用い、科学的手法で精細な定量を実現しました。安全性も重視し、刺激性のあるエッセンシャルオイルを扱う際にはラテックス手袋を着用するなどの対策講行了されました。レシピは個人のかぶれやすさや感度に合わせて調整され、カフェインが省略され、クエン酸と人工甘味料(当初はナトリウムシклаマートとサッカリンで 1:8 の希釈比、後に「Syntez-Cola」バージョンではスクラロースとバニリン)を使用しています。一部のロットでは若干の苦みが残る事例もありました。さらに、血橙、ライム、アーモンド油によるアルモンド風味のドリンク(マザパンのようなニュアンスを持つものも)などの追加作品も作成されました。実用的な課題として、ハンドミキサー由来のプラスチックのカスを濾し取ることや、ガラス製または金属製の容器の利用が検討されました。著者は自家製コーラをデカフェ Coca-Cola より優れていると評価しており、「味が薄い」と述べています。レシピバージョンは記録されており(例:blinry orange 0.1.1、blinry almond 0.1.1 など)、2026 年には Open Soda のウェブサイトが閉鎖された後、プロジェクトは新ブログと Git リポジトリを通じた詳細な変更ログやバージョニングされた配合を公開する透明性の高いデジタルリソースへと進化しました。このイニシアチブは、趣味家のための再現性のあるレシピや、ダイエットに意識的な消費者のための高度な炭酸飲料(スーパーマーケットの標準品にはない独自の風味プロファイルを備えたもの)を提供します。

「ドゥーム(Doome)」というゲームが、カーリング(Curling)の盤上でプレイされている様子。 | そっか~ニュース