乙Py先生のプログラミング教室

乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

今回はGoogleの新しいAIエージェント搭載エディター
Antigravity のご紹介です


解説動画はこちら




Google Antigravityとは

Google が 2025-11-18に発表した
エージェントファーストの開発プラットフォーム(IDE)
VS-CODEがベース



主な特徴

VS-CODEと同様のインターフェースで
エージェントファースト開発体験

エディタ・ターミナル・ブラウザを横断して
タスクを自律的に遂行できる



マネージャービューとエディタビュー

「マネージャービュー」では
複数ワークスペース・複数エージェントを一元管理し
各エージェントの進行状況や成果物を俯瞰できる

「エディタビュー」では
従来のコード編集画面に近い操作感の中で
エージェントとの対話や修正も行える



インストール方法

以下のサイトよりダウンロード
ダウンロード先


対象OS
MacOS
Windows
Linux

MacOSは
ダウンロード後に
imgファイルを開いてドラッグ



利用方法

ソフトウェアを開くのみ

初回起動時
Agent利用方法は選択式
「Agent-assisted development」 を選択

Agent利用はGoogleアカウントが必要
未作成の人は要作成
アカウントある人はログインが必要



日本語化の方法

1
左側メニューのExtensionから
Japanese Language Pack for Visual Studio Code
を探してインストールする

インストールが失敗して手動でインストールする場合は
MS-CEINTL.vscode-language-pack-ja-x.xxx.x.vsix
を手動ダウンロードしてから

Extensionメニューの ... マークから
Install From VSIX を選択してファイルを選択してインストールする


2
Shift + Command + P でメニューを開き
Configure Display Language を選択
対応言語から ja を選択して再起動する



その他設定

ブラウザーのChrome 拡張機能である
Antigravity Browser Extension 
をインストールしておくと便利(Chrome用)
(ブラウザー実行時にAntigravityが起動する)

回答を日本語にしてもらう

Agentパネル右上の「…」から
「Customizations → Rules → +Global」で
「日本語で回答してください」と設定



使い方

Editorモード
初期使用はこのモード
VS-CODEと同様のインターフェースでコードを編集する

利用できるエージェント
gemini 3 pro(High)
gemini 3 pro(Low)
Claude Sonnet 4.5
Claude Sonnet 4.5 (Thinking)
GPT-OSS 120B(Midium)


利用モード
Planning : 複雑タスク向け
FAST : シンプルタスク向け



エージェントの利用料金

現在は無料のパブリックプレビュー版のため料金なし
ただし、利用制限はあり
(クォータは5時間ごとにリフレッシュされるが)



Agent Managerモード

「⌘E」で「Agent Manager」に切り替えできる
今回は利用方法は割愛



早速作ってみる

gemini pro で回答してもらいました

数独ゲームを作ってみるプロンプト

数独ゲームを実行することのできる
sudoku.htmlを作成して下さい

以下の仕様にてJavascriptで数独ゲームを作成してください

# 仕様
画面中央に 9x9 マスの数独版を表示し
初期配置は30マス分を表示する
入力用の数字選択用の入力ボックスを数独版の下に配置する
入力ボックスは1~9の数字と、消去用のボタンを配置する
入力ボックスの数字を選択した状態で
数独版のマスをクリックすることで
数独版の該当マスに
入力した数字を表示する
数独版の該当マスをクリックすることで
数独版の該当マスの数字を消去する
全てのマスを埋めることで数独ゲームを完了する
数独ゲームを完了したら
数独版の下に数独を完了したことを表示する

リスキーダイスを作ってみるプロンプト

以下の仕様にてJavascriptでサイコロゲームを作成してください
risky_dice.htmlを作成して下さい

# 仕様
画面中央に 正20面体 のサイコロを表示する
サイコロの1面だけが「大凶」、残りの19面が「大吉」と文字列を表示させる
サイコロの下に「サイコロを降る」ボタンを配置して
クリックしたらサイコロを降ることができる
サイコロを降ったらサイコロの面の文字列を表示する
「大凶」の場合はゲームオーバーのポップアップを表示する



まとめ

まだプレビュー版のため
エージェント利用枠などが低く
お試し利用しかできていないですね

ただgemini pro を無料でも
IDEで使用できるのは大きいです

エージェントモードの詳しい使い方などが
分かり次第、また解説していきたいと思います。






今回はドラクエ1・2リメイククリア記念として
ドラクエ4のコインバグについてやっていきたいと思います。


解説動画はこちら



ドラクエ4とは

ドラゴンクエストIV 導かれし者たち
1990年2月11日にエニックス(合併前)から発売された
ファミリーコンピュータ用ロールプレイングゲーム
『ドラゴンクエストシリーズ』の第4作目

5つの章に分かれたシナリオ、AIによる戦闘システムや
5人以上の仲間キャラクターと同時に
冒険できる馬車システムが導入された作品

ゲームの2,3,5章にはミニゲームとしてカジノがあり
そこではコインバグが使える部分がありました




ドラクエ4のカジノシステムと「コインバグ」

ゲーム内の2,3,5章でいける大都市エンドール
この都市にはカジノがあり、コインが買える

1枚の価格
2章 : 10ゴールド
3章 : 200ゴールド
5章 : 20ゴールド

第3章ではコイン83,887枚を184ゴールドで
第5章では838,861枚を4ゴールドで購入できる

これはワークエリアの算術オーバーフローに起因する判定処理のバグが原因

購入金額の計算を3バイトで行っているため、カジノでコインを購入する際
計算金額が16,777,215ゴールドを超えるとオーバーフローが発生
(2 ** 24 = 16777216)

本来のコインの金額と16777216との差が
購入金額となっていたようです



ファミコンの当時のハードウェア情報


CPU:Ricoh 2A03(6502の派生)
メモリ構造:8bit CPU・少ないRAM

参考 : PS5のCPU:64ビット、スーパーファミコンのCPU:16ビット

ソフト開発はアセンブリ言語を使用
数値の表現方法(8bit × n バイトで多バイト値を管理)
多バイト演算が必要(24bit/32bitの値を手動で扱う)

スプライト制限・PPUなど
制約だらけの開発環境だったようです



ファミコン的な多バイト計算のしくみ


当時の 8bit CPU は一度に扱えるのは 0..255(1バイト)だけ
大きな数は複数バイトに分割して管理する

例:24ビット値は
LO(下位8ビット)
MID(中位8ビット)
HI(上位8ビット)という3バイトで保持


「足し算」は常に低位バイト→高位バイトの順に行い
各段で キャリー(繰り上がり) を次の段へ渡す:
LO = LO1 + LO2 → もし >255 なら carry=1 で LO &= 0xFF
MID = MID1 + MID2 + carry → 同様に繰り上げ
HI = HI1 + HI2 + carry → 最終的にさらに繰り上がれば(HI が 8bit を越える)

その上位のバイトが必要になるが、実装次第で捨てられる


乗算」は 8bit CPU で直接1回の命令で出来ないため
一般に次のどちらかで実装:

1.部分積の加算(schoolbook):
各バイトを8bit×8bitで掛けて(最大16bitの部分積)
適切に桁(バイト)シフトして加算する

2.繰り返し加算(加算を price 回繰り返す):
result = 0;
for i in range(count):
    result += price のようにして加える
(実際はより効率的なシフト+加算で実装されることが多い)


オーバーフローが起きる理由

ゲーム側が「結果を格納する領域」を
3 バイト(24ビット)だけ確保していると
数学的に 24 ビットを超える値は
自動的に上位ビットが切り捨てられる
(つまり結果は result mod 2^24)
これがバグの本質



838,861 × 20 がなぜ 4 になるか

1.コイン枚数(24bit)をバイトに分解:
コイン枚数 = 838,861
LO = 205 = 0xCD
MID = 204 = 0xCC
HI = 12  = 0x0C
検算: 12 * 65536 + 204 * 256 + 205
= 786432 + 52224 + 205 = 838861


2.各バイトの部分積を求める(コイン価格 = 20)
p0 = LO * 20 = 205 * 20 = 4,100 → hex 0x1004
  →bytes = [0x04, 0x10, 0x00, 0x00]
p1 = MID * 20 = 204 * 20 = 4,080 → hex 0x0FF0 → shift 1 byte
  → bytes = [0x00, 0xF0, 0x0F, 0x00]
p2 = HI * 20 = 12  * 20 = 240   → hex 0x00F0 → shift 2 bytes
  → bytes = [0x00, 0x00, 0xF0, 0x00]

3.部分積を下位バイトから順に加算(変数 acc は 4 バイトで保持):

初期
acc = [0x00, 0x00, 0x00, 0x00]

加算 p0
acc = [0x04, 0x10, 0x00, 0x00]


加算 p1:
acc[0] += 0x00 → 0x04
acc[1] += 0xF0 → 0x10 + 0xF0 = 0x100 -> acc[1]=0x00, carry=1
acc[2] += 0x0F + carry -> 0x00 + 0x0F + 1 = 0x10
acc[3] += 0x00 -> 0x00
→ acc = [0x04, 0x00, 0x10, 0x00]

加算 p2:
acc[0] += 0x00 -> 0x04
acc[1] += 0x00 -> 0x00
acc[2] += 0xF0 -> 0x10 + 0xF0 = 0x100 -> acc[2]=0x00, carry=1
acc[3] += 0x00 + carry -> 0x00 + 1 = 0x01
→ acc = [0x04, 0x00, 0x00, 0x01]


4.acc を 32bit で読むと 0x01000004
( 0x01000004 = 16,777,220)

ゲームはここで下位3バイトのみ(0x000004)を保存
→ 0x000004 = 4 が実際にストアされる

数学的には 838,861 * 20 = 16,777,220 だが
16,777,220 mod 2^24 = 4 となり
格納領域(24bit)に収めると 4 になる


Pythonで再現する「疑似アセンブリ」計算コード


当時のファミコンで行われていたであろう計算を
Pythonで模したコードが以下です。
# --- ユーティリティ(24bit <-> 3バイト変換) ---
def to_bytes24(n: int):
    """整数 -> (lo, mid, hi) の3バイト"""
    mask24 = (1 << 24) - 1
    n &= mask24 # 24ビットに制限(実機での格納に相当)
    lo = n & 0xFF
    mid = (n >> 8) & 0xFF
    hi = (n >> 16) & 0xFF
    return lo, mid, hi

def from_bytes24(b):
    lo, mid, hi = b
    return lo | (mid << 8) | (hi << 16)

# --- 8bit 加算(キャリーを明示) ---
def add_with_carry(a: int, b: int, carry_in: int = 0):
    """8bitの加算 -> (結果8bit, carry_out)"""
    s = a + b + carry_in
    result8 = s & 0xFF
    carry_out = 1 if s > 0xFF else 0
    return result8, carry_out

# --- 24bit のバイト単位加算(LO->MID->HI の順でキャリーを伝播) ---
def add24(a_bytes, b_bytes):
    al, am, ah = a_bytes
    bl, bm, bh = b_bytes
    rl, c = add_with_carry(al, bl, 0) # LO を足す(6502での ADC 命令相当)
    rm, c = add_with_carry(am, bm, c) # MID に carry を足す
    rh, c = add_with_carry(ah, bh, c) # HI に carry を足す
    return (rl, rm, rh)

# --- 部分積を使った 24bit x 8bit の乗算(8bit CPU の実装を模倣) ---
def mul24_by_8_partial(count_bytes, price8):
    """
    count_bytes は (lo, mid, hi) の3バイト。
    price8 は 0..255 の 8bit 単価。
    実行は:
      1) 各バイト * price を計算(部分積) -> 最大16bit
      2) それらを適切にシフト(バイト単位)して 4 バイト長の accumulator に足す
      3) 最終的に下位3バイトだけを保存
    """
    lo, mid, hi = count_bytes

    # 各バイトの部分積(16bitまで)
    p0 = lo  * price8   # 部分積0, shift 0 bytes
    p1 = mid * price8   # 部分積1, shift 1 byte (<<8)
    p2 = hi  * price8   # 部分積2, shift 2 bytes (<<16)

    # 各部分積をバイト配列(4バイト:下位から)に展開して加算
    def to_4bytes_le(x):
        return (x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF)

    # shift としてバイト単位で位置をずらす(p1 は 1 バイトシフト、p2 は 2 バイト)
    b0 = to_4bytes_le(p0)        # p0 << 0
    b1 = to_4bytes_le(p1 << 8)   # p1 << 8  -> 実際は p1 の下位を一つ上のバイトにずらす
    b2 = to_4bytes_le(p2 << 16)  # p2 << 16

    # 4バイト幅で逐次加算(8bit CPU の ADC の連鎖を模倣)
    acc = (0,0,0,0)
    for part in (b0, b1, b2):
        acc = add_4bytes_le(acc, part)

    # 実機が保存するのは下位3バイトのみ(= acc の 0..2 バイト)
    stored24 = (acc[0], acc[1], acc[2])
    return stored24

# 補助:4バイト加算(下位からキャリーを伝える)
def add_4bytes_le(a4, b4):
    r0, c = add_with_carry(a4[0], b4[0], 0)
    r1, c = add_with_carry(a4[1], b4[1], c)
    r2, c = add_with_carry(a4[2], b4[2], c)
    r3, c = add_with_carry(a4[3], b4[3], c)
    return (r0, r1, r2, r3)


簡単に計算できるようにしたコードだとこうなります
当時のFCDQ4のコイン価格を計算してみると
# コイン価格のシミュレーションコード
def simulate_24bit_mul(count, price):
    # count を 3 バイトに分解
    lo = count & 0xFF
    mid = (count >> 8) & 0xFF
    hi = (count >> 16) & 0xFF

    # 部分積
    p0 = lo * price  # fits<=0xFFFF
    p1 = mid * price
    p2 = hi * price

    # 32bit accumulator as int
    full = p0 + (p1 << 8) + (p2 << 16)

    # store as 24bit(下位24bitを取り出す)
    stored24 = full & ((1 << 24) - 1)
    return full, stored24

print(simulate_24bit_mul(100, 20))
print(simulate_24bit_mul(838_860, 20))
print(simulate_24bit_mul(838_861, 20))
print(simulate_24bit_mul(838_862, 20))
print(simulate_24bit_mul(999_999, 20))
(2000, 2000)
(16777200, 16777200)
(16777220, 4)
(16777240, 24)
(19999980, 3222764)




まとめ

ドラクエ4当時の性能では仕方ないバグだったろうと思います。

プログラミング的には相当大変な計算をしていたので
ここまでの確認が出来たかったんだろうと思います
(デバッガーや統合開発環境なさそうだし)

今は実機の性能が上がりすぎて、こういうのは無くなってきてるので
おもしろいバグとかは少なくなってますかね。

なお1990年当時
バグの噂は聞いていて知っていたので、めちゃくちゃ助かったです。
ありがとうエニックスの人!!


PS
ドラクエ1・2リメイク最高でした
ドラクエ3・1・2の流れを埋めるサブストーリー
うるさ過ぎるサマルトリア兄弟
クソ弱いローレシア王子
幼い頃にクリアできなかったので感動しました。
ありがとうスクエアエニックスの人!!





今回は投資信託の比較をするための データ抽出についてです。


解説動画はこちら


 

投資信託の成績比較


今回は投資信託の銘柄選定のために
投資信託の比較をしていきたいと思います。

データをお借りする先はこちら

みんかぶ



データを取得する

リンク先から、比較したい銘柄を選択して
「選択したファンドを比較する」ボタンをクリックすると
銘柄比較画面に飛べるので、そこのURLをコピーしておきます。

このコードでそのページのデータが取得できます。

import pandas as pd
from bs4 import BeautifulSoup
import requests
import re

url = "ここにペースト"
res = requests.get(url)


データ化する

そのままでは使いづらいので
読み込みできる形にします。
soup = BeautifulSoup(res.text, "html.parser")
table = soup.find("table",class_=re.compile("w-full border-t border-slate-300"))
trs = table.find_all("tr")
data = []
for tr in trs:
    tds = tr.find_all("td")
    tmp = []
    for td in tds:
        tmp.append(td.text.replace("\n",""))
    data.append(tmp)
   
df = pd.DataFrame(data)
df

これでデータフレームになり見やすくなりました。
ここから、使うデータだけに絞り込み整形していきます。




ファンド名、リターン、シャープレシオのみ取得する


先ほどのまでのコードで
全体のデータが取得できていたら
必要なものののみ整形します。

今回はファンド名、リターン、シャープレシオの値を使用します。

リターン、シャープレシオの値は
1,3,5年ごとの値になっているので
正規表現でうまく抜き出してデータ化します。
fand_name = data[0]
r1,r3,r5 = [],[],[]
for text in data[10]:
    pat = re.compile(
        r'(\d+)年([+-]?\d+(?:\.\d+)?%?|-)'
        r'(?=\d年|$)'
    )
    pairs = re.findall(pat, text)
    result = {int(y): float(v.replace("%","")) if v!="-" else 0 for y, v in pairs }
    r1.append(result[1])
    r3.append(result[3])
    r5.append(result[5])
    
s1,s3,s5 = [],[],[]
for text in data[11]:
    pat = re.compile(
      r'(\d+)年([+-]?\d+(?:\.\d+)?|-)'
      r'(?=\d年|$)'
    )
    pairs = re.findall(pat, text)
    result = {int(y): float(v) if v!="-" else 0 for y, v in pairs }
    s1.append(result[1])
    s3.append(result[3])
    s5.append(result[5])

df2 = pd.DataFrame()
df2["ファンド名"] = fand_name
df2["1年リターン"] = r1
df2["3年リターン"] = r3
df2["5年リターン"] = r5
df2["1年シャープレシオ"] = s1
df2["3年シャープレシオ"] = s3
df2["5年シャープレシオ"] = s5
df2

ここでうまくファンド名と数値データのみになっていれば
可視化して比較することができます。



リターンとシャープレシオで散布図にする

今回はリターンとシャープレシオを用いて比較していきます。

シャープレシオというのは

投資信託のリターン ÷ リスク(値動きのブレ)

の値で、リスクに対して
どれだけ効率的にリターンをあげているかの目安です。

シャープレシオが高い = より効率よく運用されている
ということになります。

シャープレシオが低い  = リスクを取りすぎている
とも取れます。


一般的な目安としては

シャープレシオの値評価
2.0以上非常に優れた運用成績
1.0から2.0良好な運用成績
0.5から1.0平均的な運用成績
0.5未満改善の余地がある運用成績


ということになっているようです。

早速比較してみましょう
import plotly.express as px

fig = px.scatter(
    df2,
    x="5年リターン",
    y="5年シャープレシオ",
    color="ファンド名",
    hover_name="ファンド名",
    title="ファンド別:5年リターン × 5年シャープレシオ",
)

fig.update_layout(
    xaxis_title="5年リターン (%)",
    yaxis_title="5年シャープレシオ",
    legend_title="ファンド名",
)

fig.show()
スクリーンショット 2025-11-08 16.52.42






まとめ

銘柄が多すぎて選べない際は、リターンやシャープレシオ
手数料、信託報酬などで、色々比較するのが良いです。

同じリターンなら、シャープレシオが高いものの方が
より効率良く運用されているとみなせます。

なお短期のシャープは信用度が低いので
長い期間で見るようにしましょう。


今回はここまでです
それでは

このページのトップヘ