滅するまでの記録

滅するまでの記録

滅するまでの記録です

アプリ「クトゥルフプラス」に登録した

 前から気になっていたTRPGクトゥルフの呼び声」アプリ。ルールブックやシナリオなんかが読み放題のサブスクサービスなんですが、この度通常月650円のベーシックブランが1ヶ月限定で50円というキャンペーンが始まったので登録してみました。

 プレイする予定は無いのですが、昔から「ルールブック」とか「モンスター図鑑」とか読むの好きなんですよね!クトゥルフTRPGもルールブックは持ってるんで目当てはソースブックと呼ばれるシナリオ集とか各時代の世界観なんかを紹介してる「ソースブック」と呼ばれるものだったんですが・・そっちは月1000円のプレミアムプランでしか読めないと分かってガックリ。でもまあ、シナリオを読むのも好きなので1ヶ月たっぷりと楽しませてもらうとするか・・

 

 ちなみに「邪神診断」の結果はミ=ゴでした。でした。本当はナイアルラトホテップかイスの偉大なる種族が良かったけど、ミ=ゴも悪くないので良かったかな!

「キーチ!!子供編」を読んだ

 代表作と思われる「ザ・ワールド・イズ・マイン」が(個人的には)期待外れだったというのがあったので、正直惰性で読み始めたのだけど・・

 き、キミ、やったら出来るやんか!!

 そう、びっくりするくらいに面白かったのでした。正直、最序盤の幼稚園編はかなりしんどいんですけど、それを超えてあるポイントに達したところからは怒涛の展開。子供編のクライマックスに向けて「えっ?ええっ!?」てな感じで読む手が止まりませんでした。もともと絵も上手いし、妙な観念的ネタなんか出さなかったらとんでもなく面白い物が描ける!ってのがよく分かりました。多分、あなたは2回「ええっ!?」と声を出すでしょう、間違いない。本筋以外の注目ポイントとしては・・やっぱり「モモちゃんのキモさ」ですかねぇ(^_^;)

 子供編があまりにも良かったので、ここで一旦休止。「代紋Take2」で精神を落ち着かせてから続きを読むとします。

 KindleUnlimitedに入ってる人だったら是非読むべし!

「ザ・ワールド・イズ・マイン」を読んだ

 

 大昔、連載時にに途中まで(カップルの車をハイジャックして逃走する辺りまで)読んで脱落してしまったこの作品。今回、とうとう最後まで読み切りました・・・

 な・・なんじゃコレーッ!

 多分僕の漫読力(まんどくちから)が足りないんだろうけど、何が言いたいのかさっぱり分かりませんでした。いや、別に「言いたいことが無い」なら無いで、変な理屈をコネずに破壊と殺戮だけを繰り返す・・ってのならばそれはそれでメッセージ性あると思うんですけど、いや、マジでなんなん?って感想しか残りませんでした。

 良かったのは終盤の「トシとモンの出会い編」が、まあ本編の流れと齟齬が無かったことですかね。

 (特に)悪かったのは、モンの幼少期編。これいらんやろ!って言うか、それを書いちゃあおしまいよって感じじゃないですか?

 全体的にめちゃくちゃ緻密な部分とガバガバな部分が混ざってて評価に困るところだとは思うんですよね・・全体の流れが緻密で、ところどころ緻密ってのがなぁ・・逆だったら全然良いんだけど。

 結論!暴走は良いけど迷走は勘弁してくれ〜と。まあそれでも「渚にて」の途中棄権よりは良い・・か・・?

エレクトロカムイ11月第5週

どうも!田和龍虎です。

今週の収穫は・・

 

 バン!タコ足OAタップ、タコ足配線、マウス、スイッチのコントローラ部分(多分壊れてるんだろうけど)、HDMIケーブル。まあ・・モブキャラって感じですけど、タコ足タップなんていくつあってもいいですからね。あとHDMIケーブルも地味に嬉しい

 

 続きましてエレコムのBTワイヤレスキーボード、ゲームコントローラー2個。

 キーボード・・拾わずにはいられない!電池入れたら普通に完全動作しました。外観もキレイだし、まあ誰かにあげるのも良いだろう。

 ゲームコントローラーはもう20個くらい持ってるんですけど・・あれば回収してしまう。まあ、USB接続だしいつか倉庫を改造してゲームルームにするかも知れないので、その時に大規模マルチゲームをやるとしたら・・無駄にはならないからね。

 

 そして本日のメインディッシュ!なんと真っ二つに折れたポメラDM10(初代)。変形機構のせいでキーボードの折り曲げ部分で折れるのは、この機種で超あるある!なんだけど、逆に言うと対処法も確立しています。まあ、変形部分をボンドで無理やり固定することなんですけど。結果、スライド拡張変形は不可能になるんだけど、写真のようにノートパソコンみたいに蓋を閉めるだけ・・って形での運用は可能になると。

 重要ポイントとしてはカラーが白だったということ。我が家のDM10はオレンジと黒なのですが、この白を合わせると・・・全カラーバリエーションコンプリート!という事になるんですよね。これは拾うしかないだろうと。まだこれにポメラガンダムコラボタイプを3機(グフ1、ザク2)を持ってるので電池タイプだけでも6機か〜・・まだ1台も壊れてないのでこのままでは死蔵したままになってしまう。かといって、1ファイル8000文字という制限のある初代機を欲しい人なんておるんだろうか、と。

 今回全カラバリがセットになったことで更に手放すのが難しくなった。ガンダムコラボもグフ1、ザク2って事はこれランバ・ラル、アコース、コズンのランバ・ラル隊再現セットやん!と気づいてしまってからフォーメーションを崩すのが嫌だし・・困ったことよ。

 今回、ずっと放置されてる様子でとっくにボタン電池は切れてたのでデータは蒸発してると思ってたんだけど、電池を入れてみたところ前オーナーのデータが残ってました(即座に消しました)。ということは、これ・・ボタン電池は日付の維持に使われてるだけで本体にセーブしたテキストデータは残るって仕組みなのか!だったら子供にあげてもデータを喪失する恐れはないし、日記帳兼キーボード入力練習マシンとして使ってもらえたら惜しくは無いなぁ。買おうとは思わないが捨てるには惜しい・・罪なマシンだぜ!

プログラミング学習日記 2025/11/30 AndroidでPDFスライドショーアプリを作って(もらって)みよう

じゃあMT5行くか!?と思ってたんですけど・・そう言えば1つ忘れてた。

皆さんってちょっと人には見せられない画像の管理ってどうされてますか?なるほど、画像ファイルをフォルダに分けて、PCやタブレットで画像ビューワを開いてスライドショーモードで環境映像っぽく流してる、と、そうおっしゃる。

なるほどね!でも・・ベストの中のベスト、これは絶対に外さない!って画像の場合、PDFにまとめてしまって管理したい・・そう思ったことはないですか?どっちでも良いですか?でも、PDF管理には明確なメリットがありまーす!

それはファイルの移動。ファイルの移動では細かいファイルが多数ある場合、極端にコピー・移動速度が落ちてしまうというデメリットがあります!(経験者ならば分かりますね?)。PDF形式でまとめてしまってれば、これを防ぐことが出来る(ハズ)なのです。

ところが意外なことにPDFファイルをスライドショーのように、任意のインターバルでフリーハンド(両手が使えるに越したことはないからね!)で流す事が出来るソフトが、ホントーッに無いのです。嘘でしょ?このアプリ大氾濫の時代に?

 ということでAntigravityに頼んで作ってもらうことしました(もちろんコードを読んで学習にも使います)。

 条件はこんな感じ。手持ちの時代遅れのタブレットでも使えるように、と。

 

 Antigravityが選んだ拡張機能はコレ。あ、AndroidってJAVAなんだ?触ることは無いと思ってたのにアンドロイドアプリには必要なのか~

 

 即座にコードは生成されたんだけど、実行やらApkファイル作成にはAndroidStudioってのが必要とのことでダウンロードしてます。

 

 ダウンロード中にコードをチラ見。よくわからんが作られたコードはKotlinって言語で書かれているらしい。JAVAを入れた意味は?

 

 あ、そーなのか!じゃあ今後アンドロイドアプリを作ることになる事もあるだろうから覚えておこう。でも、GoogleなんだからGoで統一してくれると良いのだが?(Goが気に入ってるので)

 

 なるほどね・・確かにGoはゲームエンジンも謎の個人制作のEbitenginってのに頼ってるしなぁ

 

 短いコードだしせっかくなので後でじっくりと読みます

 

package com.example.pdfslideshow

import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.pdf.PdfRenderer
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.ImageView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import java.io.IOException

class MainActivity : AppCompatActivity() {

    private lateinit var pdfImageView: ImageView
    private lateinit var btnOpenPdf: Button
    private lateinit var etInterval: EditText
    private lateinit var btnToggleSlideshow: Button

    private var pdfRenderer: PdfRenderer? = null
    private var currentPdfDescriptor: ParcelFileDescriptor? = null
    private var currentPageIndex = 0
    private var totalPages = 0

    private var isSlideshowRunning = false
    private val handler = Handler(Looper.getMainLooper())
    private var slideshowRunnable: Runnable? = null

    private val openPdfLauncher = registerForActivityResult
(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            result.data?.data?.let { uri ->
                openPdf(uri)
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        pdfImageView = findViewById(R.id.pdfImageView)
        btnOpenPdf = findViewById(R.id.btnOpenPdf)
        etInterval = findViewById(R.id.etInterval)
        btnToggleSlideshow = findViewById(R.id.btnToggleSlideshow)

        btnOpenPdf.setOnClickListener {
            stopSlideshow()
            val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                addCategory(Intent.CATEGORY_OPENABLE)
                type = "application/pdf"
            }
            openPdfLauncher.launch(intent)
        }

        btnToggleSlideshow.setOnClickListener {
            if (isSlideshowRunning) {
                stopSlideshow()
            } else {
                startSlideshow()
            }
        }
    }

    private fun openPdf(uri: Uri) {
        try {
            closePdf() // Close previous if any

            val fileDescriptor = contentResolver.openFileDescriptor(uri, "r")
            if (fileDescriptor != null) {
                currentPdfDescriptor = fileDescriptor
                pdfRenderer = PdfRenderer(fileDescriptor)
                totalPages = pdfRenderer?.pageCount ?: 0
                currentPageIndex = 0

                if (totalPages > 0) {
                    showPage(currentPageIndex)
                    btnToggleSlideshow.isEnabled = true
                    Toast.makeText(this, "PDF Loaded: $totalPages pages",
Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "PDF has 0 pages",
Toast.LENGTH_SHORT).show()
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
            Toast.makeText(this, "Error opening PDF", Toast.LENGTH_SHORT).show()
        }
    }

    private fun showPage(index: Int) {
        if (pdfRenderer == null || totalPages == 0) return

        val page = pdfRenderer!!.openPage(index)
       
        // Create a bitmap with the page's dimensions
        val bitmap = Bitmap.createBitmap(page.width, page.height,
Bitmap.Config.ARGB_8888)
       
        // Render the page onto the bitmap
        page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
       
        // Display the bitmap
        pdfImageView.setImageBitmap(bitmap)
       
        page.close()
    }

    private fun startSlideshow() {
        val intervalStr = etInterval.text.toString()
        val intervalSeconds = intervalStr.toLongOrNull() ?: 3L
        val intervalMillis = intervalSeconds * 1000

        if (totalPages == 0) return

        isSlideshowRunning = true
        btnToggleSlideshow.text = getString(R.string.stop_slideshow)
        etInterval.isEnabled = false
        btnOpenPdf.isEnabled = false

        slideshowRunnable = object : Runnable {
            override fun run() {
                if (!isSlideshowRunning) return

                currentPageIndex++
                if (currentPageIndex >= totalPages) {
                    currentPageIndex = 0 // Loop back to start
                }
                showPage(currentPageIndex)
                handler.postDelayed(this, intervalMillis)
            }
        }
        handler.postDelayed(slideshowRunnable!!, intervalMillis)
    }

    private fun stopSlideshow() {
        isSlideshowRunning = false
        btnToggleSlideshow.text = getString(R.string.start_slideshow)
        etInterval.isEnabled = true
        btnOpenPdf.isEnabled = true
        slideshowRunnable?.let { handler.removeCallbacks(it) }
    }

    private fun closePdf() {
        try {
            pdfRenderer?.close()
            currentPdfDescriptor?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        pdfRenderer = null
        currentPdfDescriptor = null
        totalPages = 0
        btnToggleSlideshow.isEnabled = false
    }

    override fun onDestroy() {
        super.onDestroy()
        closePdf()
    }
}

 これを

 

 インストールしたAndroidStudioにフォルダごと読み込ませまして、その後どうすれば良いのかAntigravityに質問

 

 いや、実機を使ったデバッグとか良いから、いきなりインストール可能なApkファイルを出力して欲しいとゴネたいところ。

 

 Antigravityで質問して、エラーが出たらエラーメッセージをAntigravityに貼り付けて修正をお願いして・・とか面倒だなぁ・・と思ってたんですが。なんともタイムリーなことに新しくAndroidStudioにもGeminiを使ったAIサポート機能がついてたという!

 

 (Antigravityに書いてもらった)コードでエラーが出てApkが作れなかったので、Studioのチャット欄を使ってエラー修正を依頼。

 

なんかアイコンの画像を用意してないのに指定してたのが原因だったらしい。なので、Antigravityのコーディング能力に問題はなかったといっても良い。

 

 アイコン画像とか面倒なだけなので適当に作ってくれないか?と依頼したがさすがにそれは無理だった。まあ、アイコンとかいんのでそのままでビルド依頼

 

 Studioの全体映像はこういう感じ。これは・・アンドロイドアプリが作りたい場合はAntigravityを使う必要は全く無いな。もしもこれで一発で動くアンドロイドアプリなんかが出来たりしてしまった日には・・グーグルプレイ(アプリストア)にAIで作られた大量のアプリが溢れかえることになりそう・・・ま、でもそうそう一発で動くなんて事は・・

 

 動いたーっ!UIは説明不要のシンプルさ。数字の部分はインターバルの秒数でOpenPDFボタンで端末内のPDFファイルを選択、Start SlideShowボタンでスライドショー開始・・・

 凄すぎやろAI!

 それでも・・それでも僕は手打ちのコーディングしかプログラミングとは認めたくないけどね!

プログラミング学習日記 2025/11/29 Antigravityに書いてもらったDestinyQuest1のコードを読む

 Antigravityの使い心地が良いので、英訳だけにしておこうと思ってたDestinyQuest1のシステム作りを戯れに依頼してみることにしました。

 

 例によってシステム自体はすぐに作れてるようなのだけど、検証作業に入ってスタックしてしまってるような感じ・・さすがにそこまで進歩はしてないんだな・・と感じつつテストは自分でするからと中断。

 

 ちなみにAntigravityがすごいのは、AIの力で構造化するんじゃなくてちゃんとPythonで変換用コードを書いてるっぽいんですよね

 

 Antigravityの制限が来たので、そう言えば・・と無料で使えるAI統合エディタ「Void」を久々に起動。今までAI機能を全く使ってなかったのだけど、同じように無料で使えるのかな?と

 

 比較用に同じ英文のテキストファイルを用意して依頼してみると・・

 

 ズコーッ!まさか自分でやれと言われるとは・・

 

 じゃあすでに翻訳済みのファイルでシステムをコーディングするのはどうだ?と思ったんですが・・ステップを説明することは出来ますが、実際の作業は自分でやれというw Void・・アンインストールしました

 

 Antigravityで書いてもらったDestinyQuest1のコードを実行してみる。これがちゃんと動くんですなぁ・・

 

 戦闘システムが動くのかをチェック。ちゃんと動いてます

 

 何回やっても勝てないな?と思ったらパラグラフの初期値が1になってるので難易度が高いマップから始まってるんですよね。DestinyQuest1では最初に全体マップがあって難易度別のスタート地点を選ぶというシナリオ選択の柔軟性があるのです

 

 じゃあ書いてくれたコードを読んでいくか!まずはTOMLファイルから読み込んで、GameState構造体を初期化。

 

 LoadGameData関数も見てみよう。ファイルのオープンとかクローズとかは・・まあコピペすれば良かろう。

 

 この方式はLonewolfで書いてもらった方法とはまた違うっぽいけど・・

 

 こういう流れで対応した構造体に当てはめていくってことか。でも、変数がErrだけなのは?ErrがNilかどうかだけをチェックしてるんだろうか?

 

 結論から言うとエラーチェックしてるだけでした

 

 結局マッピングしてるんだったら、実際のマッピングと一緒にすれば良いのでは?と思うのだが

 

 え?あ、なんだそうか返り値(エラーの時のみ)を返すメソッドなのか

 

 続いて各種構造体。GameStateはシンプルで、Lonewolfでは泥縄で足していたフィールドだけど文字列をキーにしてBoolの値を持つテーブルで管理するってことか。フィールドCombatはCombat構造体ってのを作って管理するのか・・

 

 中身をチェックしてみると・・なんだLonewolfで言うところのEnemy構造体だったのか。もっと戦闘システムに関わるような複雑なものを想像してワクワクしたのに。でも実際のところ、ここに特殊ルールフィールドを追加すれば特殊な敵モンスターにも対応できるわけだな

 

 Storiesがシナリオデータだけど、インデックスで呼び出せるようにスライス形式で管理するってことですな。なるほど・・Combat.WinNextIDが存在しない場合には次の敵がまだ存在するということか・・考えとるねぇ!

 

 例によって選択肢がない通常ノードの場合にはゲームオーバーって扱い。

 

 なるほど、ちゃんとMapシステムへ移動する仕組みもあると

 

 ForはRangeばっかり使ってたので、こういう普通?の使い方は新鮮。なるほど、;で区切って、初期値、条件、インクリメントでWhileみたいに使えると・・あれ?以前同じような事を書いた気がする。

 

 戦闘メソッド。初期化をして・・

 

 ん?Reader.ReadStringの使い方が、前の部分と違うけど

 

 なんだ、Hit Enter Keyを実現するための書き方ってことか

 

 まあ、このへんは仕組みをそのままコード化してるだけ

 

 そうそう、DestinyQuest1の戦闘ルールってちょっと変わってて攻撃速度勝負で勝った方だけが毎ラウンド攻撃できるんですよね。つまりSpeed命。戦闘後HPは全快するのも特徴かな。

 

 Antigravityを使ってのドルアーガ、DestinyQuest1のコード生成とリーディングでお腹一杯になったので、気分転換にLonewolfのデバッグをしつつ、次のプログラミング学習に移りますか!もちろん、Antigravityには今後も活躍してもらおう。MT5に挑戦するか!

 

ツカーサさんの記録の最新作「メカンダーロボOP」が素晴らしい

 久々に更新が来たので・・

www.youtube.com

 デビルマンはちょっと手抜き感があったので(やっぱり段ボールロボットでないと)、久々に王道復活って感じで楽しませてもらいました。合体シーンがすごい!

 

www.youtube.com

 しつこいけど僕の一番のお気に入りはやっぱりブライガーですけどね!