SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|12|
2025|01|

2024-12-06(Fri) 自分を見詰め、字を詰める

  ここしばらく、Factorioに夢中なのだが、仕事の方でも実にエキサイティングである。一部の勢力からの自分の評価が極めて低いことを知ってしまったのである。

  しかし、一時的には腹が立ったものの、しばらくして、その勢力からの評価が低いことは、むしろ望ましいことなのではないかということに気づいてしまった。その勢力の考え方は、決して自分の信念と相容れないからだ。一方で、自分が好きな人、尊敬する人たちからは変わらず手放しでの高評価を受け続けているのだし、まぁ、それでいいかと。

  自分としては、マズローの4段階目「承認の欲求」で自身について納得しつつ、5段階目「自己実現の欲求」を半ば達成しつつあるような心境だ。乱れた心も整いつつあるのを感じる。

  考えてみれば、生殺与奪の権利を持っているのはこっちなのだ。金もあるし。それで自ら好きな仕事だけしてればいい立場になったのだから、それが一部からの低評価との引き換えだとしても別にいいやと。自分の信念に背くような仕事の仕方すんの、ヤだしね。それさえ無理なら、袂を分かつさ。

  なんだかキャプテンハーロックの気持ちってこうだったのかもと思えてきた。どんな大勢力が相手だろうと、俺の旗の下に、俺は自由に生きる。それがアルカディア。それが男。それでいい。

  そんなこんなで、片や落胆しつつ、いつも以上に楽しんで仕事をしているのだが、その中で「字詰め」処理を行う必要が生じてしまった。Rubyで。長い一文を、例えば18文字、で折り返す処理である。これが意外と難しい。え!? そんなん、1行じゃん?

print(target.gsub(/(.{18})/, "\\1\n"))

  ……って思うかもしれんが、それは甘い。

 その話をカミさんにしたら、そんなの
借りときゃいいのに、という反応。いや
、そりゃそうだけど、2km弱を乗った
ところで何になるってんだ、などといい
つつ、なぜかRotary-EVの素晴
らしさについて語りだしてしまうオレw
。ほとんど乗らないワシらにはオーバー
 :

  まず、英文字と漢字とは幅が違うので、同じ1文字と数えると右端が揃わない。それよりなにより「禁則処理」がなされない。句点(。)や読点(、)が行頭に位置してしまっている。そんなの気にしないという向きもあろうが、自分はイヤだ。そんな処理もできないのかと思われるのは恥ずかしい。

  しかし、これが意外と難しい。組版するわけではないので、次行への追い出しのみの実装でよいのだが、先頭から処理していくと、遡るような処理が必要になるのだ。例えば、

「その時、彼は『こんにちは(笑)。』と書き込んだの。」

  を、18文字で詰めると、

「その時、彼は『こんにちは(笑)。』
と書き込んだの。」

  だが、17文字で詰めると、

「その時、彼は『こんにちは
(笑)。』と書き込んだの。」

  1文字でなく、5文字も減らす必要が生じるのだ。

  こんな典型的な処理、どこかで誰かが書いているだろう、と思って検索してみたが、良いものが見つからない。思い返せば、彼の「電脳倶楽部」の創刊号で祝一平編集長自らがPDSとしてCで書いて公開した「wrap.x」がまさにそれなのだが、コードを入手する手段がない。当時はまるで興味をそそられなかったプログラムだったが、ディスクマガジン用のリーダを書こうと思えば、そら一番に必要になるコードだよなぁ。いまさらに深く納得。

  それなりにうんうん唸って、何度か書き直して、そこそこ納得できるコードが書けたが、丸一日かかってしまった。正規表現とかを駆使すればもっとシンプルかもしれないが、そう得意ではないし、ハナモゲるのがイヤなので、使っていない。

class String
 
    def char_wide
        self.getbyte(0) < 0xE0 ? 1 : 2                          # 文字の表示幅を返す
    end
 
    def each_wline(w_max)
        loop {
            c_max = 0; w = 0; self.each_char {|c|               # 仮分割位置を算出する
                w + c.char_wide > w_max and break
                c_max += 1; w += c.char_wide
                c == "\n" and break
            }
            if(self.size > c_max)                               # 1 行分を適切に切り出す
                c = c_max.times {|cr|
                    # 行末禁則文字
                    '([{「『([{'\
                        .include?(self[c_max - cr - 1, 1]) and next     # 仮分割位置の直前(行末)の文字
                    # 行頭禁則文字
                    '),.]}、。」』っッー)]}'\
                        .include?(self[c_max - cr, 1]) and next         # 仮分割位置の直後(行頭)の文字
                    break(c_max - cr)
                }
                yield(self.slice!(0, c))
            else                                                # 残りを返す
                yield(self)
                break
            end
        }
    end
end
 
target.each_wline(38) {|l|
    puts("\t|%s|" % l)
}

  結果はこう。

	| その話をカミさんにしたら、そんなの借|
	|りときゃいいのに、という反応。いや、そ|
	|りゃそうだけど、2km弱を乗ったところで|
	|何になるってんだ、などといいつつ、なぜ|
	|かRotary-EVの素晴らしさについて語りだ|
	|してしまうオレw。ほとんど乗らないワシ|
	|らにはオーバースペックすぎるんだって、|
	|なんていいながら、サイトで見積もりまで|
	|してしまう。天井が黒くって、ロードス|
	|ターとお揃いじゃん。並べたらいい感じじ|
	|ゃない? ……って、そらそうだけど、ホ|
	|ラ、500万だってよ。500万ってさ、1年を|
	|働いてようやく余るかっていう金額だよ。|
	|MX-30のために1年働くって思ったら……い|
	|や、そういうのも意外と悪くないんじゃな|
	|いかw。オレ、チョロいなぁ……。
|
	| 前にも言ったが、代車を貸すほど効率的|
	|なプロモーションはないよなぁ。あー、|
	|やっぱり借りて、明日の仕事はブッチして|
	|しまうべきだったなぁ。人生で最高レベル|
	|の失敗をしたかもしれん……。
|

  関数名の「each_wline」の「w」は「wrap.x」へのリスペクトである。

  書いていて気づいたのだが、既に「半角、全角」という世ではないこともあって、ネット上にコードが見つからないのかもしれない。プロポーショナルな環境の場合、自分で詰めるべきではなく、CSSなどを使うべきなのだろう。

  ついでに、コードを書いたあと、思いついてChatGPTにコードを書かせてみたが、なぜか行頭禁則しか扱ってないし、動かしても何の処理もされなかった。やはり元となるようなサンプルコードがある場合限定なのかもしれん。

  あ、ちな、上記のコードには、一部の禁則文字しか定義していないし、分離禁止文字も実装していない。あしからず。


2024-12-08(Sun) 続・自分を見詰め、字を詰める

  自分語りっぽい内容だが、まぁ、ブログってのは日記なんでね。

  思い返せば、小学校の5年だ。担任が社会の先生なので、社会の授業内容が一風変わっていた。丸1年かけて「瓦」の製造を行う「自動工場」と「伝統手工業」の両者について、見学、インタビューを通じて、比較していくというものだった。それは、今でも強く覚えているくらいに、自分には楽しい取り組みで、追加のインタビューに行くメンバの募集にも手を挙げ、担任の車で「伝統手工業」の作業場を再訪したりした。

  次は、中学校の2年だ。音楽の授業で「アイネ・クライネ・ナハトムジーク」の冒頭を探してきてテープにダビング、印象をまとめて発表すること、というチーム課題が出た。自分は、ちょうどSHARPのX1を買ってもらってしばらくした頃だったので「冒頭部分はオレが作るから」とMMLを書いてPSGで鳴らしてテープに録音し、発表に臨んだ。ところが、PSGの音色はファミコン以下なので、曲が流れ始めるなり授業は爆笑の渦。先生は「面白いね、ちょっと間違ってる音もありましたけど」と苦笑い。しかし、忘れられないのは、その直後に誰かに言われた「お前、音楽5だぜ」だ。

  別に「やってみたかったからやった」だけで、そんな狙いは一切なかったので、そう言われても「そんなもんかな」としか思わなかったが、気づけば、上述の小学校の時も似たようなことがあった。いま考えると、そういう「点稼ぎ」にフォーカスして生きている人間は、その頃からいたし、その年齢でそうだということは、おそらく親もそういう人間なのだろう。別に「悪いこと」とまでは言わないけれど。

  ここ数年「歌」を続けている。カラオケでの練習の際に「機械採点」機能を使っているが、それにフォーカスして歌うことはない。だって歌の本質は「聴衆へのメッセージ」だろ? 誰かに聴かせることは滅多にないけど、その軸足を動かすことはない。絶対に。

  例え話だが、自分はいつものストリートで歌っている。少しずつファンもついてきて、みんな楽しんでくれている。中には、歌い出す前から、帽子の中に千円を入れてくれる人もいる。それに対して、今日も最高の歌を聴かせるぜ、と心に誓うことはあっても、明日も千円を入れてくれますか、と尋ねることはない。た・の・ま・れ・て・も、決してそれはしない。それでストリートの管理者からの評価が下がろうが知ったことか。それは信念だ。それが通せないのであれば、そのストリートを去るまでのことだ。ファンには申し訳ないけれど。

  とはいえ、悲しいかな最近の日本では、本質よりも「点稼ぎ」へフォーカスしている団体は珍しくもないんだよな。警察とかマスコミとか……歪んだルールルールルールゥー……。

  さて、昨日の字詰めの続きだ。実は昨日のコードは「AIの回答内容」を折り返して表示させたい、という用途で書いたものなのだが、よく考えたら「幅xx文字で字詰めして答えて」とAIに指示する方法もあるのではないか、と思いついた。

  で、早速、ChatGPTで試してみた。

Q. 野球のルールについて、400文字程度、漢字で幅19文字で字詰めして、説明してください。
 
A. 以下は、幅19文字で字詰めした野球のルール説明です。
 
野球は九回まで攻守を交
代し得点を競う競技です。
攻撃側は打者が投手から
投じられる球を打ち、一
塁から三塁を回り本塁へ
戻ることで得点します。
一方守備側は三つのアウ
トを取ると攻守交代とな
ります。打者はストライ
クを三つ取られるか、飛
球やゴロを守備側に捕球
されるとアウトとなりま
す。攻撃側は打者が出塁
し次の塁を狙い、走者と
して得点を目指します。
最終的に得点が多いチー
ムが勝者となります。

  おい。全部で200文字にも満たないし、幅も11文字じゃねーか。「19文字になっていませんが。」とやり直させたが、なぜか12文字に変更した挙げ句に「各行が正確に19文字になっています。」と断言してきた。どういう勘定なんだ、それは。ダメだこりゃ。ま、そういうのが不得意ってことはわかったよ。

  一方で、ここしばらくは手元で動かすAIにも興味を持っている。先日「3.1-Swallow」ってのがいいよ、と聞いたので試してみることに。ところが、ggufをダウンロードできる場所にたどり着いたものの、似たようなファイル名のサイズ違いが20個以上も並んでいるではないか。どうやら量子化という、いわば圧縮レベルを変えたものが、ズラズラと並んでいるらしい。

  あまり詳しくはないが、LLMの基本はベクトルの集合。浮動小数点の組で表現されているそれを整数化することにより、圧縮するのが量子化らしい。それにより、精度は低下するが、サイズが小さくなり、演算も速くなる。そういえば、最近のCPUにはNPUというAI向けの演算器が乗っているらしいが、速度のために精度を犠牲にした演算器だと認識している。それと同じことか。

  詳しくはわからんが、なんでも、Q5_K_M, Q5_K_S, Q4_K_Mくらいが、バランスのよい量子化レベルらしい。知らずに最初に試したIQ1_Mには「こんにちは。」を無限に繰り返されたしなw。なるほど。

  つうわけで、3.1-SwallowのQ5_K_Mに尋ねてみた。

# docker exec -it ollama-alpha ollama run Swallow-8B-Instruct-v0.1-Q5_K_M
>>> 野球のルールについて、400文字程度、漢字で幅19文字で字詰めして、説明してください。
野球は9人ずつの二チームが交互に攻撃と守備を行う競技です。攻撃側は打者1人と走者3人(一塁、二塁、三塁)を出し、相手の守備側から点を得ることを目指します。守備側は投手1人が投球し、他の8人が守備位置につき、打者の打撃を阻止したり、走者をアウトにすることで攻撃を終わらせます。
 
試合は9イニングで行われ、各イニングは3アウトになるまで続けられます。3アウトになると攻守が交代します。攻撃側は投手の投球を受けて打撃し、走者が出た場合には次の塁を目指して走ります。
 
守備側は投手にボールを投げさせて打者の打撃を阻止したり、打球を処理してアウトにすることで攻撃を終わらせます。走者がアウトになると攻撃は終了し、次のイニングに移行します。
 
野球では、守備側が3つのアウトを取るか、攻撃側が得点を挙げて勝利するかのいずれかで試合が決まります。
 
>>> Send a message (/? for help)

  幅の指定は無視かよ! まぁ、400文字程度にはなっているけど。こういうのが不得意なのは、AIに共通する性質なのだろうか。

Q. ひろゆきっぽい感じでお願いします。
 
A. 以下は「ひろゆき」っぽい論調を意識しつつ、19文字幅に整形した野球ルールの説明です。
 
野球って九回まで攻守交代
して得点を競う競技なんで
すよね。でも、実際これっ
て守備がアウト三つ取った
ら交代になるんですよ。攻
撃側は球を打って一塁から
三塁回って本塁に戻ると得
点になるんですけど、守備
側が飛球とかゴロ捕ったら
アウトなんですよね。で、 
最終的に点数が多い方が勝
ちなんですけど、これって
時間かかりすぎじゃないで
すか?もっと効率的な方法
とか考えないんですかね?

  爆笑w。最後は説明じゃなくて、あなたの感想ですよねw。こういうの、ChatGPTは得意だよなぁ。これはもう、ホント賞賛しかない。

  ……あ、最後に字詰めについてその後に気づいたことだ。まさにこの行のように、しばしば自分は行頭を「……」から始める表現を用いることがある。って、言いつつ、まさにこのように、しばしば自分は文を「っ」から始める表現を用いることもある……これ、どちらも行頭禁則に抵触する表現だ。が、これはアリって気がする。

〜〜を「……」から始める表現を用いることがある。
って、言いつつ、まさにこのように、しばしば〜〜

  一方で、昔から笑いながらの発言を示す表現に(笑)がある(笑)。が、それも最近だと草生える、という表現が一般的だw。新しい表現なので、句点の扱いに困るのだが自分は句点の前に付けている。まぁ、正式な表現ではないからルールなんてなく、好きにさせてもらうwww。が、この場合に限って「w」は行頭禁則がふさわしい気がするのだよな。つまり、これはイヤだ。

〜〜いからルールなんてなく、好きにさせてもらう
www。が、この場合に限って「w」は行頭禁則が〜〜

  そういうことを言い出すと、英語圏のスマイリーはどうするんだとか、キリのない話になっていく。結局、アルゴリズムではなく、状況に応じた字詰めが必要ということか。まぁ、いいか;-P。


2024-12-16(Mon) なんとなく亀岡へ走り出す

  いろいろな意味で遂に仕事に見切りをつけてしまったので、少し休むことにした。丸々一週間。理由は、充電でも、自分探しでも、仕事探しでも、有給消化でも、メンタル不調でもよいが、有給に理由はいらないのだよな。特段の理由はないが、あえて言うなら「時間を食べるテスト」だろうか。

  そのテストならば、ずっと家にいる方がよいような気もするが、せっかくならロードスターの相手もしたい。なんとなく、嵯峨嵐山のトロッコ列車を思い出し、行ってみることにした。今回はあえてユルい計画で行く。決めたのは亀岡の駅前の宿だけ。トロッコ列車に乗るかどうかも気分次第。

  ユルい気分が過ぎたのか、カメラも布チェーンも忘れてしまう。ある意味で計画通りか? 23号から421号へ、石榑トンネルを抜け、昼時になったので、道の駅「奥永源寺渓流の里」へ。鹿カレーというのを見つけたので食う。意外と肉が多くて得した気分。

  平日なので市街地は常に渋滞気味だ。近江大橋を通り、山中越で京都市街へ。っと、そこで左から目の前に車が飛び出し、ABSが作動するほどのハードブレーキ。こっちは見ていたので、まだ余裕はあったが、1秒先を走っていたら避けられなかったかも。アホタレが。

  金閣寺の横を抜け、道を間違えて清滝トンネルへ。福井のトンネル群みたいな信号があったので、先が行き止まりと知りつつ長らく待って通ってみたが、やはり廃線トンネルの転用らしい。戻りは上の峠を通ったが、恐ろしく凸な道路をメクラで通らされた。後で見たら上にミラーがあったらしい。そんなの気づかんて。その後、そこそこヤバげな道を抜け、保津峡駅へ。

  画像の説明 画像の説明

  特段、なにがあるわけでもない。トイレを済ませて北へ。ここから西、亀岡方面へは、線路はあるが道路はないので、かなりの大回りが必要なのだ。引き続き、そこそこヤバげな道が続くが、いきなり集落が現れる。水尾という町らしいが、スゴい場所もあったもんだ。さらに先に進むと、路肩に雪が現れ始める。ちょっと焦ったが、どうにか北へ抜けた。

  田舎道を南下し亀岡へ。デカいサッカースタジアムがあり、駅周りだけ都会のふりをしているヘンテコなトコだ。駐車場は24時間で250円。とりあえず、ホテルにチェックインし、夜の散歩に出る。

  特段の当てはないが、SFでも読みたい気分なので本屋へ。文庫を作者名順に並べるのはヤメろよ、と思った矢先、ハヤカワ文庫が平積みされているのを見つけた。珍しいこともあるもんだ。「AIとSF」というアンソロジを見つける。安野貴博、野崎まど、野尻抱介と並んだら買うしかない。ちょっと高いがレジへ。新品の紙の本を買うのはしばらくぶりだ。にしても、ちょっと厚すぎないか。

  ホテルのレストランで晩飯。枝豆、熱燗、豚の生姜焼き定食、大盛。風呂はまぁまぁ。ベットで読書。で、野崎まどの作品に爆笑。AIって、SFって枠で捉えると、どうしても悲観的になりがちだけれど、未熟なAIの感覚のズレを楽しもうってのは、発想が好き。SF要素は後付けっぽいけど。

  さて、明日は何をしようか。とりあえず、早く起きるために、早く寝ることにしょう。


2024-12-17(Tue) なんとなく大阪へ走り出す

  6時に目を覚ます。朝食は6時45分から。ここんとこロングドライブの宿は、開店と同時がデフォだ。パパッと食べて駐車場に向かう途中、フロントガラスをガリガリしている人を見かける。え。そんなことになってんの? と、思ったら、なってた。ガリガリするの面倒なので、エンジンをかけデフロスタをマックスにして待つ。まぁまぁ溶けたら出発。でも、幌は凍りついているので開けられない。残念。

  画像の説明

  今日は大阪に行ってみることにした。ちょっと前に計画した「パークアンドライド」を試してみようかと。目的地は日本橋付近。だいぶ前に行ったレトロゲーセン「ザリガニ」と、共立「シリコンハウス」でも再訪するつもりだ。

  山道を門真市に向かって南下する。雪が心配だったが大丈夫。酷道かと思ったら、そこそこ交通量があり、連なって走ることに。が、その先、市街地に入るとかなりの渋滞だ。なぜか茨木駅前を通過。摂津の新幹線基地の横を通り、なんとか門真市へ。

  知ってはいたが、駐車料金は平日なので550円ではなく990円。ガード下なので無闇に狭く、あまり快適とは言えない。門真市から京阪電車で北浜へ。280円。大阪の電車ってなんか違うな。北浜での乗り換えは地下で完結。案内が多く迷わない。堺筋線に乗り換えて恵美須町へ。240円。んが、恵美須町駅を上がった交差点にある駐車場が、当日800円、休日でも1800円だと!? なんやそれ。パークアンドライドの意味ないやんけw。まぁ、市内の渋滞は避けられるし、電車も楽しいけど、別に必要なかったんかいな。あほらしわぁ。

  気を取り直して、レトロゲーセン「ザリガニ」へ。なんだか活気がない。そりゃ、平日だからな……と、いうのともなんか違う印象。いきなりテトリスが目を引く。画面の色ズレで四角の黄色ブロックが3x2みたいに見える。これはキツそう(笑)。アフターバーナーが3台並んでいるのはスゴいよな。アウトランもダライアスもある……けど……あれ? アウトラン、なんだか画面がスクエアだ。ちょっと笑えない。

  画像の説明 画像の説明

  やっぱりダライアスはやっておくべきだよな……が、1P側のコンパネが使えないので2P側でプレイしろという状態。海外版ならではの逃げ方だな。まぁ、右側にいること多いんだし、いいか。ボタンが連射付きなのはいいが、連射なしボタンはなし。コインも入りづらい。うーん、呑み込まれないだろうなぁ……入った。あれ、中央のモニタの色がヘンだ……んー、なんだか? なんだか??

  プレイを始めてすぐにレバーの反力がユルいことに気づく。ユルいどころか、時々、下に入りっぱなしになる。実はこの症状、ダライアスの純正レバーにありがち。だから、前回来た時はシャキッとした反力に、むしろ驚いたんたものだが……これは狭いステージに行くのが怖い。エキストラバージョンではないようなので、ミサイルで止める。なんとなく、保険を多めに5つで止めた……ところで、中央のモニタの色がヘンなのは、赤成分が抜けているから、ということに気づく。これだとカプセルの色がわからん。赤かと思うと取るのを躊躇してしまう。

  「A-C-F-J-O」と下ルートを進む。青をいくつか逃したので、いまだスーパーアームのまま。ほんじゃ、と、Tを抜けて、Yに進んだ。狭いトコは無事に抜けられたが、イカの直前でアームが剥がれた。これはキツい。正面切って戦うほど若くはないので、上の腕に張り付くが、痛恨のミス。正面でも戦ってみるが、ヤズカがキツい。すべての残機を潰し終了。まぁ、そんなもんか。

  2Fに上がってみたが、サイバリオンもドルアーガも動いていない。と、ようやくそこで、疑念が確信に変わった。きっと、飽きちゃったんだなぁ、これ。動いていない筐体が端に固められている。どれも、稼働するに満たない状態なのだろう。メンテナンスを怠ると、こういうことになるんだなぁ。

  画像の説明

  レトロゲーセンって、全国に数えるほどしかなくなってきてるけど、その各々は想像以上の情熱によって支えられていたのだ、ということを実感させられた。ゲームセンタなんて、基本は個人が営利のために運営しているわけだから、その運営状態は運営している個人の裁量に依存する。ある意味で「底」を見ることができて、当たり前のことに気付かされた。まぁ、自分もレトロゲーは好きだけど、飽きずに何年もメンテし続けられるかといわれると必ずしも自信ない。

  対象がパブリックになっていると誤解しがちだが、法に反しない範囲で次第に腐っていっても、法は止めてはくれないのだ。それは大きな会社だろうが同じ。個人経営はもちろん、イエスマンに囲まれている経営者は、方向性を誤ってもそれに気づけない可能性が高いだろう。もっと大きく、業界だろうが、国家機関だろうが、それは起こりうる。それを個人の働きかけで変えることは難しい。できるのは、それを正しく捉えて振る舞うことくらい。たった100円で多くのことに気づくことができたな。さらばザリガニ。これが最後だ。二度とここには帰らん。

  次の目的地である共立「シリコンハウス」へ。最近、ハンダゴテを振るってないので、そんなにワクワクしないし、買うものもない。そういえば、マウスのセンタークリックが腐りかけてたことを思い出し、タクトスイッチだけ買っておく。33円。6個で198円。と、その近くでアーケードゲーム用のジョイスティックとボタンをみかける。ちょっとイイな。でも、4方向レバーに変更できるヤツが欲しいんだよな。帰ったら探してみよう。やっぱり、実店舗はインスピレーションを与えてくれて、イイ。

  さて昼飯時だが、お好み焼きや、たこ焼きや、串かつは、どうにも気が進まない。寒いこともあり、見かけた喜多方ラーメン坂内で済ませた。大阪に来た意味ないなぁ。せめて、それっぽい場所を見て回ろう、と、道頓堀を抜けて、淀屋橋まで歩く……が、さすがにちょっと歩き過ぎてつかれた。さほど得るものもないし。京阪急行で門真市のクルマに戻る。期待はしなかったが、manacaだったので、やはりパークアンドライド割引はなし。ちぇ。

  画像の説明

  門真市からは知った道だ。最近やたらと寄る道の駅「お茶の京都みなみやましろ村」で休憩。名阪国道。伊賀ICで下りて旧25号。あとはいつもの道。19時着。

  旅はいいな。必ず収穫がある。まぁ、旅には限らないけれども。


2024-12-19(Thu) Fedora41でrakipicしてsdccする

  先日、なんの気なしに共立「シリコンハウス」に行ったのだが、久々にハンダゴテを振るいたくなった。チギれていたテスタのICクリップを付け直し、PICの開発環境を整え始める。ここ数年やってなかったしな。今でも似たような手順で整えられるものだろうか。

  画像の説明

  いまのFedora41の前、Fedora30の時も一応は整えていたようだが、記事としてはもうひとつ前のFedora24だな。まずは、rakipicで、秋月のPICライタの健康チェックをしてみる。

raven.itline.jp: /root/rakipic # git clone http://itline.jp/git/rakipic
raven.itline.jp: /root/rakipic # cd rakipic
raven.itline.jp: /root/rakipic # ./install

  部品箱をゴソゴソして、使い古しの16F648Aを見つける。PICライタにセットして読んでみる。

raven.itline.jp: /root/rakipic # rakipic -p 16F648A -r

  なんか息継ぎするな。最後まで行かずに止まってしまうし。

raven.itline.jp: /root/rakipic # rakipic -p 12F629 -r

  8ピンPICは大丈夫だ。

raven.itline.jp: /root/rakipic # rakipic -p 16F648A -r

  別の個体なら大丈夫だった。そんなことあんのか?

raven.itline.jp: /root/rakipic # rakipic -p 16F819 -r

  こっちもOK。今でも型番は忘れていないな。16F688も試したかったが、治具が見当たらず。捨てはしないが、どこにしまい込んだのだろう。

  次はコンパイラ環境。まずは、ワザと失敗させる。

raven.itline.jp: /root/rakipic # make all
sdcc-sdcc -V --use-non-free -mpic14 -p16f648a -c blink_led_16f648a.c
make: sdcc-sdcc: そのようなファイルやディレクトリはありません

  アセンブラとコンパイラを導入。んが、ヘッダファイルが足りない。

raven.itline.jp: /root/rakipic # dnf install gputils
raven.itline.jp: /root/rakipic # dnf install sdcc
raven.itline.jp: /root/rakipic # make all
blink_led_16f648a.c:2:123: fatal error: pic16f648a.h: No such file or directory

  例の、フリー、非フリー問題か。非フリーのスイッチを落とし、パッケージを再ビルドする。

raven.itline.jp: /root # dnf download --source sdcc
raven.itline.jp: /root # rpm -ivh sdcc-4.1.0-11.fc41.src.rpm 
 
raven.itline.jp: /root # vi rpmbuild/SPECS/sdcc.spec 
raven.itline.jp: /root # diff rpmbuild/SPECS/sdcc.spec.org rpmbuild/SPECS/sdcc.spec
< %configure --enable-doc --disable-non-free  STRIP=: ${OPTS} PYTHON=python3
> %configure --enable-doc STRIP=: ${OPTS} PYTHON=python3
 
raven.itline.jp: /root # dnf install bison boost-devel flex latex2html lyx texinfo inkscape
raven.itline.jp: /root # rpmbuild -ba ~/rpmbuild/SPECS/sdcc.spec 
 
raven.itline.jp: /root # rpm -ivh ~/rpmbuild/RPMS/x86_64/sdcc-4.1.0-11.fc41.x86_64.rpm 

  再ビルドに1時間くらいかかったが、とりあえず前と似たような手順で導入できた。例によって、ヘッダファイルの調整が必要だったが、なんとか16F648AでLチカに成功した。

  画像の説明

  しかし、久々に秋月のサイトでPICを見て驚いた。軒並み値上げしている。こんなに高かったっけ? 16F688については、以前の日記に「120円という価格が魅力だ」って書いてあったが、それが330円だとは。まぁ、便利で高機能なチップなのだから値段相応と言われればそんな気もするが。こないだの半導体不足とも関係あるのかしらん。個人的にはだいぶ買い貯めてあるので、死ぬまで買わずに済みそうな気もするが、ちょっとPICを見る目が変わってしまったな。

  さてさて、特段、必要に迫られているわけではないが、リハビリのために照度/温湿度計でも作ってみようかしらん。


2024-12-20(Fri) ヴァリアブル・ジョイスティック「JS-2V」製作開始

  先日、なんの気なしに共立「シリコンハウス」に行ったのだが、アーケードゲーム用のジョイスティックとボタンをみかけ、また作りたくなってしまった。だいぶ以前にそれなりのものを作ったのだが、あれは8方向レバー固定なのだ。4方向レバーに変更できる仕様のものがほしい。

  品揃えは千石がよいようだが、いまひとつ仕様がハッキリしない。セイミツのオフィシャルサイトと見比べながら、どうにかモノを選定した。ジョイスティックはLS-32のSEベースというもの。もっともオーソドックスな仕様だということで選んだ。

  画像の説明 画像の説明

  裏側には複雑に穴の空いたプレートが装着されており、装着する場所をズラすことにより、レバーの動きを8方向、4方向、2方向に物理的に制限することができる。ちょっとテスタで調べた限りだが、4方向モードでは、絶妙に斜めに入らないようになっている。つまり、縦と横のスイッチが同時にONにならないのだ。まさに欲しかった仕様だ。

  欲を言えば、パネルを外しても8方向レバーを回すと引っかかりがあるのが残念。滑らかにレバーを回すことはできない。そういうプレートも別売されているかもしれないが。とりあえず残りの人生でザンギエフを使いだすことはないと思うので、大丈夫。あ、バルトリックには有用かな。

  ついでにレバーボールとボタンも買った。ボールはLB-35というもの。35φで黄色。オイラの軸足はナムコだからね。ボタンはPS-14-GNというもの。例によって3つ。グラディウスはやりたいが、格ゲーはやらないので。30φで黄色。オイラの軸足はナムコだからね。

  以前のヤツは最終的にだいぶゴチャゴチャした感じになったが、今回はUSB対応だけでいいかな。おあつらえむきの木箱を探さなければ。


2024-12-28(Sat) WebSocketクライアントを実装する

  しばらく前にMezatalkというチャットツールを作り、職場で活用している。チャットツールといえばWebSocketだ。発言の送信や受信には必須の機能である。通常、発言はブラウザのJavaScriptから行われる。が、Ruby版のコマンド「wsclient」も用意してある。ボットに発言させたい場合などに使える。こんな感じだ。

res = system('./wsclient',
	'ws://127.0.0.1:33109/',
	"{:REQUEST=>'login', :TYPE=>'talk', :USER=>'user1', :ROOM=>'_t~room1'}@@login",
	"{:REQUEST=>'sentence'}@@Hello.",
)

  実際、先日に記事にしたXalebotは、その名の通りボットであり、問い合わせに対する回答案などをMezatalkに発言するという連携機能も備わっている。

  で、今回「特定の発言が行われたら、別のシステムでその発言を処理する」という機能を実装する必要が生じた。まずは、別のシステムから発言を取得できるようなAPIを実装し、WebSocket経由で発言を取得できるようにした。さらに、発言が行われた場合に、設定ファイル中に記述した関数を呼び出す機能を実装し、そこから「wsclient」を実行しようとした、のだが……それが、どうやっても動かない。普通にコマンドとして実行すれば動くのだが、設定ファイル中からだと動かない。にっちもさっちもよっちもごっちも動かない。ドハマリ。

  「wsclient」は「em-websocket-client」というRubyのライブラリを使っているのだが、見よう見まねで書いたコードなので、それ以上に追求のしようがない。うーむ、こうなったら、独自に実装するか。

  というわけで、RFCの6455「The WebSocket Protocol」を眺めつつ、チマチマと実装していく。HTTPで接続後、プロトコルを切り替えたり、クライアントからの送信内容にはマスクを施したり、なんかいろいろと珍しい処理がある。面倒クサいが面白い。面白いが面倒クサい。

  そしてデキたのがコチラです。

#!/usr/bin/env ruby
 
require './wshelper'
require 'timeout'
require 'socket'
 
wsh = WebSocketHelper.new(uri = ARGV.shift)
 
Timeout.timeout(3) {
    sock = TCPSocket.open(wsh.uri.host, wsh.uri.port)
    sock.syswrite(wsh.handshake)
    sock.sysread(9999)
 
    while(request = ARGV.shift)
        sock.syswrite(wsh.encode(request))
        puts('[%s]' % wsh.decode(sock.sysread(9999)))
    end
    puts('Closed.')
}
require 'uri'
 
class WebSocketHelper
 
    attr_reader :uri
 
    def initialize(uri)
        @uri = URI.parse(uri)
    end
 
    def handshake
        (<<REQ % [@uri.path, @uri.host, @uri.port, make_websocket_key]).gsub(/\n/, "\r\n")
GET %s HTTP/1.1
Host: %s:%s
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Key: %s
Sec-WebSocket-Version: 13
 
REQ
    end
 
    def make_websocket_key
        nonce = []; 4.times { nonce << rand(0xFFFFFFFF) }
        @websocket_key = [nonce.pack('N*')].pack('m0')
    end
 
    def encode(req)
        make_masking_key
        head(req) + payload(req)
    end
 
    def make_masking_key
        @masking_key = rand(0xFFFFFFFF)
    end
 
    def head(req)
        head = ''
 
        fopc = 0
        fopc += (fin = 1) << 7
        fopc += (opcode = 1)
        head << [fopc].pack('C')
 
        mplen = 0
        mplen += (mask = 1) << 7
        if((it = req.length) < 126)
            mplen += it
            head << [mplen].pack('C')
        elsif(it < 65536)
            mplen += 126
            head << [mplen, it].pack('Cn')
        else
            mplen += 127
            head << [mplen, 0, it].pack('CNN')
        end
 
        if(mask == 1)
            head << [@masking_key].pack('N')
        end
 
        head
    end
 
    def payload(req0)
        len0 = req0.length; req = req0.dup
        req << "\x00" while(req.length % 4 != 0)
        res = []; req.unpack('N*').each {|u32|
            res << (u32 ^ @masking_key)
        }
        res.pack('N*')[0, len0]
    end
 
    def decode(res)
        fopc = res.slice!(0, 1)
        mplen = res.slice!(0, 1).unpack('C')[0]
        if(mplen < 126 and mplen == res.length)
        elsif(mplen == 126 and (res.slice!(0, 2).unpack('n')[0]) == res.length)
        elsif(mplen == 127 and (res.slice!(0, 8).unpack('NN')[1]) == res.length)
        else
            raise('Unexpected.')
        end
        res
    end
end

  まぁ、ヤルことちゃんとヤッてない。んが、仕事はキッチリこなします。まるで、オレみたいなヤツだな。んが、やっぱり、設定ファイル中から呼ぶと動かない。にっちもさっちもよっちもごっちも動かない。な、なんでぇ?

  終いにはtcpdumpでパケットまで確認してしまう。要求は出ている。んが、応答が返らない。設定ファイル中から呼んだ場合だけ。なんだこれ。いや、正確にはタイムアウトした瞬間に応答が返る。なんだこれ。なんだこれ。なんだこれ。サーバ側の問題?

  サーバ側は「em-websocket」というRubyのライブラリを使っているのだが、見よう見まねで書いたコードなので、それ以上に追求のしようがない。うーむ、こうなったら、独自に実装するか……って、イヤ、それはオオゴトすぎるべ。さすがに、これまで4年近くも動いているコアの部分はイジるべきではないだろう。

  そこでようやく気がついた。サーバ側に追加した、このコード。

it = @configs[:post_paragraph_hook] and it.call(room)

  これ、connection.onmessageの延長、つまり、コールバック関数の中で動いている。そんなトコで、さらに「wsclient」で要求を出して応答を待ったって、サーバ側もそこで待っとるっちゅーねん。つまり、要求を処理するヤツが、要求を出して応答を待ってたって、応答するヤツはテメエ自身なんだから、応答が返るわけがない。ぷふゎぁ〜……。

  長らくプログラミングしているが、こんな状況は初めてだなぁ。じゃ、どうすりゃいいか。こうすりゃいいだけだ。

it = @configs[:post_paragraph_hook] and Thread.new {
    it.call(room)
}

  ちゅーわけで、動くようになった。結局、元の「wsclient」が使えたので、WebSocketクライアントの独自実装は徒労に終わった。でも、結果として目的は達成できた。結果オーライ。

  けどね。エンジニアの諸君には言うまでもないだろうが、技術力というのは膨大な徒労によって培われるものなのだ。見える成果だけを成果とするならば、それは当たり馬券以外には金を払わない、と主張するのと同じ。

  しかし、久々に悩ませられたなぁ。そのぶん、動いたときの嬉しさは大きかった。これだからプログラミングは、やめられまへんなぁ。


2024-12-29(Sun) ヴァリアブル・ジョイスティック「JS-2V」を製作

  さて、しばらく前にセイミツの業務用アーケードスペックのジョイスティックとボタンを購入したのだが、ガワの手配に悩んでいた。金属ではなく木材がいい。手が冷えるのはイヤだし、加工もラクなので。で、昨日、ホームセンタで材料を買ってきた。

  画像の説明

  手頃なサイズでフタ付きの木箱が欲しかったのだが、そんなに都合の良いものはなかった。しかたなく、手頃なサイズ、300x200のMDFパネルを天板にすることにし、足として端材をくっつけることにした。端材の幅は45。ジョイスティックのメカ部の格納には40チョイの高さが必要なので45はちょうどいい。178円と58円。仮組み用でも構わない値段だ。

  まずは足を付ける。端材の両端を150で落とし3分割。MDFパネルの裏に両面テープで位置決めして、皿タッピングで固定。寿司下駄ができあがった。元の端材の加工精度は高く、ほぼガタ付きはない。これ、普通にイケるなぁ。

  Φ29のホールソーでジョイスティックを取り付ける穴を空ける。ジョイスティックを固定する周囲のネジはΦ5。とりあえず適当なネジで固定したが、ここには後でステンのトラスネジを奢りたいな。

  自分の指の配置に合わせボタンを取り付ける穴を空ける。ここでもΦ29のホールソーを使うが、必要なのはΦ30。仕方ないのでヤスリとペーパで穴を拡げる。それなりに大変だったが、どうにか取り付けられた。思った以上にいい仕上がりだ。こういう工作も上達したなぁ。

  画像の説明

  次は乗っ取り工作。ちょうど表面が加水分解を起こして腐りかけていたゲームパッドがあったのでそれを使うことにする。BUFFALOのBSGP1601BKというもの。5年チョイ前に買ったもののようだが、なぜ買ったのか覚えていない。

  バラすと基板は2枚。面倒なことに、乗っ取りたい十字とトリガの接点は、メイン基板じゃない方にある。面倒くさいので、適当なタッパーの中に放り込んでUSBの配線をやり直しつつ、グラウンドを探し、十字と3つのトリガ、合わせて8本の線を引き出す。しかし、またもやパターン中には、各接点の途中にハンダを乗せられるパッドがある。乗っ取ってくれといわんばかり。なんなの、これ? 法律で決まっているの?

  画像の説明 画像の説明

  本体側に戻る。ピンヘッダで接続するために、それを固定する基板が必要なので、だいぶ前に作った基板を流用する。元はFT245AM用だが、もう使うことはないだろう。木材にタップを切ってネジで止める。そこからは空中配線。ヒドい手抜きっちゃー、手抜きだが、実際のアーケードゲームも、パネルを開けたらこんな感じになってるはずなので、気にしない。

  画像の説明

  さて、テスト、なのだが、Linuxの場合、コントロールパネルにジョイスティックの項目がない。が、dnfで探したら、ソレっぽいのがスグに見つかった。「jstest-gtk」と、そのものズバリだ。もちろん、動作はバッチグー。完成。やっほー。

  画像の説明

  4方向レバーを試すため、パックマンをプレイしてみる。うむ。良い感じである。続いて、3トリガを試すために、グラディウスII。4方向レバーではゲームにならないが、トリガの動作は良好。ならばヨシ。最後に、本命のドルアーガの塔……だが、下が効かない。よく見たら、コネクタが断線していた。今日はここまで。明日、直そう。

  久々に丸一日、楽しく工作することができた。が、老眼が進んでいるので、メガネの取り替えが面倒くさい。また、熱中して作業をしていると、固定した姿勢が続くのでちょっと筋肉がダルくなる。ここ数年、カラダはそれなりにガンガンと鍛えているのだが、固定した姿勢からのヨッコラセには効かないんだなぁ。歳には逆らえないなぁ。


2024-12-30(Mon) ヴァリアブル・ジョイスティック「JS-2V」を改良

  朝っぱらから、昨日作ったジョイスティックを修理してドルアーガしてみる。やはり4方向レバーはよいな。久々すぎて、宝の出し方があやふやになっているが、ふわふわっと19フロアまで。改めて挑戦しよう。

  ちなみに、4方向レバーが、どれくらい4方向レバーか調べてみた。昨日の「jstest-gtk」を使って、ゆっ〜くりとレバーを回す。すると、上半分は微妙にナナメに入る箇所があり、下半分は微妙にどちらにも入らない箇所があった。まぁ、機械的な制限であるから、パーフェクトはないんだが、十分に4方向している。すぐれものである。

  んが、やっぱり途中にタッパーを挟むのは、ものすごく使いづらい。両者をつなぐケーブルも短すぎた。というわけで、メイン基板の方に結線する形にして、ジョイスティック本体に一体化した。

  画像の説明

  アナログスティックは取り除いてしまおうと思ったが、ギリ高さが足りたので残してある。まぁ、こんな位置に付いていたって使いようがないけれど。

  しかし、やっぱりアケコンはいいなぁ。でも、今の若いもんには響かないんだろうなぁ。ゲームセンタの筐体にも、ゲームパッドがつながっているものがあったりするくらいだしなぁ。

  さて、自分の中で4方向レバーといえば、ドラゴンバスターなのだが、気がつけば大/魔界村やスパルタンXも4方向レバーなんだよな。なかなか新しい視点だなぁ。

  ちなみに、今回かかった費用。ジョイスティック3200円。ボール250円。ボタン400x3円。送料660円。MDFパネル178円。端材58円。あとは全部手元にあった道具と部品なので、合計5546円。


2024-12-31(Tue) 2024年の終わりに

  大晦日ということで、なにか総括しようかと思ったが、特には思い浮かばない。

  が、今年は、いわゆる「振り返り」をするクセが付いた気がする。週で、月で、年で。先日、安野貴博氏の講演を聴いて印象に残ったのが「振り返りはコスパがいい」だった。講演を聴く前からクセが付き始めていたので、妙に納得してしまった。「振り返り」自体は、当然「新たな取り組み」ではないけれど、それにより過去を評価でき、よりよい未来を選べる可能性が高まる。

  つうわけで、自分で今年のブログを見返してみると、欠かさずプログラミングしているなぁ。ジャンルは様々だ。よいなぁ。満足だ。小学6年から続けているから、42年弱。電子工作も、もう21年半になるのか。

  大きなドライブもしてるなぁ。まだまだ走りたい道はある。免許取ってから、34年チョイ。運転がイヤだった瞬間はない。ロードスターに乗り始めて丸7年。運転が楽しくなかった瞬間はない。

  ヴォーカルも進歩したなぁ。始めて6年チョイ。まだ伸びる気がするし、楽しく続けられている。

  ジョギング、筋トレも、始めて4年半。それほど苦もなく続いている。これ以上マッチョ度を増すつもりはないので、維持に努めているが、ちゃんと維持できている

  モデルガンは……一過性だったな。それなりに楽しかったけど、趣味としての発展性はないな。また、ヒマになったら手を出すかもしれんが。

  それと、41年前に出会った友人との旧交が復活したのは嬉しかったな。キャンプもなかなか楽しかった

  それにつけても、今年、特に感じたのは、本質を忘れた「団体」の多さだな。警察はだいぶ前からだが、耳目(クリックや視聴率)を集めることだけにフォーカスしてしまっているマスコミ。先日の航空機事故も含め、大概の事故は小銭をケチって当然の仕事(メンテナンス)を怠った結果、起きている。自分の所属している会社もそうだ。点稼ぎ主義。見ているのは客ではなく、上役や予算や数値になってしまっている。小銭の欲しさに客のトラブルを願うサポート部門って……控えめに言ってクソ以外の何物でもない。もう、あきらめた。が、トヨタだって、クルマの良さよりも、作りやすさやコストダウンにフォーカスして失敗した時期があるのだ。大会社だって普通に誤りうる。そもそも、日本国民の平均年齢は既に自分より下。歳と分別は必ずしも比例しないが、それでも若者ほどバカである傾向はあるだろう。自分もバカだった。

  それでも、別に何かができるわけではない。人は変えられない。自分が変わるほかない。

  さーて、来年は何をしようかなぁ。クソを眺めていたってイイ事はない。ウキウキできることは探せばあるはずだ。