寄り道しながら27時間ぐらいでクリア。
街中の人を採用できるというのが斬新だが、キャラに思い入れしにくいというのは本当にそう。
今後の AI 技術の発達によって、音声合成・会話合成がより進めばもっと自由度が生まれて楽しくなるかもしれない
色んなキャラがいるが、そんなにめっちゃいいスキルがあるかというとそうでもないので、採用しようっていうモチベが薄い
もう少しレアスキルもちとかいればな〜
育成要素があるともうちょい楽しいと思うけど、ごちゃごちゃしちゃうかも。
結局、SMG/LMG 持ってるやつが便利でそういうのばっかり使っちゃう
ハッキングしていくのは純粋に楽しい
流石にリリースから数年たってるのでバグはほぼ感じなかった
良くも悪くも UBI ゲームって感じで、良ゲー、ぐらいの感じ。
ストーリーがちょっと弱いというか、先が見えちゃうかも。
kotlin の dataframe 的なライブラリである krangl が deprecate されていた。
kotlinx.dataframe が後継なので、これを利用するようにするとよい。
ドキュメントが整備されていて、読みやすくなっている。dataframe 周りも kotlin で扱いやすくなって最高だ。
https://kotlin.github.io/dataframe/overview.html
kravis は kotlinx.dataframe ベースに変わっていた。
神ゲーだと思う。Atomic Heart でがっかりした直後だから余計にそう思うのかも。。
STANDARD 28.2時間 で B。Window gaming PC で Steam 版をクリア。弾がなくなることはほぼないかな。
ホラーというよりアクションって感じ。虫がいきなり出てきたところ以外で驚くことはないかな。
原作プレーしてない勢というか、スマホ版とかを昔ちょっとやったけど途中で操作性悪くて辞めた勢。
freemarker の js_string がどのような変遷をたどってきたのかをまとめてみた。
2.3
Date of release: 2004-June-15
https://freemarker.apache.org/docs/versions_2_3.html
New built-ins for Java and JavaScript string escaping: j_string and js_string
2.3.1
Date of release: 2005-01-04
https://freemarker.apache.org/docs/versions_2_3_1.html
The js_string built-in now escapes > as > (to avoid ).
2.3.20
Date of release: 2013-06-27
https://freemarker.apache.org/docs/versions_2_3_20.html
Bug fix [390] (and other improvements): ?js_string and ?json_string didn't escape the u2028-u2029 line terminators (problem for JavaScript) and the u007F-u009F control characters (maybe a problem in JSON, depending on implementation). Furthermore, the escaping of , <, and > become safer in that now they are escaped whenever it can't be guaranteed that they won't be part of <!, ]]> or </. Earlier they were only escaped when it was known that they are part of these patterns, thus it was possible to assemble these patterns from two adjacent interpolations. Additionally, from now on <? and --> also count as dangerous patterns, and will trigger < and > escaping.
まとめ
2.3.20 以後、2023年4月現在の最新版である 2.3.32 まで変更はなし。
commons-io で IOUtils.toString(inputStream, StandardCharsets.UTF_8)
としているケースは、Java 9 以後なら new String(inputStream.readAllBytes(), StandardCharsets.UTF_8)
と書けて便利。
https://www.baeldung.com/convert-input-stream-to-string
https://openapi-generator.tech/docs/templating/#all-generators-core
openapi-generator で enum を制御するにはこのへんを参照。
x-enum-varnames
: 変数名
x-enum-descriptions
: description
という2つを使って構成する。
varnames はともかくとして descriptions は OpenAPI の spec に入っておくべき要素だと思う。
https://github.com/OpenAPITools/openapi-generator/issues/6190
openapi-generator は ServiceLoader で generator を探すので、CLASSPATH に custom generator を突っ込むことができれば良いです。
CLASSPATH にカスタムジェネレータを突っ込むには buildSrc
を使えば良いので、例えば以下のように階層構造を作れば良いですね。
.
├── build.gradle.kts
├── buildSrc
│ └── src
│ └── main
│ ├── java
│ │ └── my
│ │ └── own
│ │ └── Generator.java
│ └── resources
│ └── META-INF
│ └── services
│ └── org.openapitools.codegen.CodegenConfig
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
buildSrc/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
の中に my.own.Generator
の名前を書けば読み取られます。
AbstractJavaCodeGen あたりを継承して以下のようにメソッドを上書きすれば抑制できる。
@Override
public boolean getUseInlineModelResolver() {
return false;
}
Mac OS においてデバッグするときには、NSLog を呼ばないととにかくやりづらい、というときがあります(InputMethodKit を使った開発をするときなどが特にそうです)。
そんなときには、以下の様にすると良いでしょう。
use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
#[link(name = "Foundation", kind = "framework")]
extern {
pub fn NSLog(fmt: id, ...);
}
fn main() {
unsafe {
NSLog(NSString::alloc(nil).init_str("Hello, %@"), NSString::alloc(nil).init_str("world"));
}
}
Dependency は以下のように Cargo.toml に記述します。
[package]
name = "rust-nslog-sample"
version = "0.1.0"
edition = "2021"
[dependencies]
cocoa = "0.24.0"
さて、とはいえ毎回 NSString だなんだと書くのはめんどくさい、というのはあるかも。
せめて最初の引数はマクロ化したいですよね。
↓マクロ化するならこんな感じかな?
use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
#[link(name = "Foundation", kind = "framework")]
extern {
pub fn NSLog(fmt: id, ...);
}
macro_rules! NSLog {
( $fmt:expr ) => {
unsafe {
NSLog(NSString::alloc(nil).init_str($fmt))
}
};
( $fmt:expr, $( $x:expr ),* ) => {
unsafe {
NSLog(NSString::alloc(nil).init_str($fmt), $($x, )*)
}
};
}
fn main() {
unsafe {
NSLog!("Hello, %@ %d", NSString::alloc(nil).init_str("world"), 5963);
}
}
オープンなかな漢字変換エンジンを開発しているということを先日お知らせしました。
https://blog.64p.org/entry/2023/01/16/032912
そのあと、順調に開発が推移しまして、いい感じにいろいろな機能が盛り込まれました。日本のOSSでは29日に定期リリースするという監修があります(最近聞きませんが。。)なので、今日 v0.2.0 を出しました。出したと言っても tag を打っただけですが。
https://github.com/akaza-im/akaza/releases/tag/v0.2.0
モデルファイルが別のレポジトリに分割されたこと
ローマ字テーブルがハードコーディングからYAMLファイルに追い出されたこと
ログファイルを fern 管理にしたこと
AZIKに対応したこと
設定画面を実装したこと
ライブコンバージョンモードを実装したこと
など、利用するにあたって基本的な機能を一通り実装した感じになります(ほかにもいろいろあるのだけど、おおすぎて書ききれない)。
特に大きな変化として、外部辞書を利用できるようになったことがあります。SKK 形式の外部辞書が使えるようになったので、何らかの新しい語彙が必要になった時は、SKK形式の辞書を利用させてもらえばよいのです。圧倒的な安心感ですね〜。
多分、割とインストールして試せるぐらいにはなってきたかなーと思います。ので、試してみていただけるとありがたいです。
本バージョンを出すまでに omasanori さん、tkng さん、pklionさんにご協力いただきました。ありがとうございました!
こんな感じで実装できる。真面目にやればもうちょいちゃんと書けるけどこんな感じで。。
use std::cmp::min;
const NUMS: [&str; 10] = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
const SUBS: [&str; 4] = ["", "十", "百", "千"];
const PARTS: [&str; 18] = [
"",
"万",
"億",
"兆",
"京",
"垓",
"𥝱",
"穣",
"溝",
"澗",
"正",
"載",
"極",
"恒河沙",
"阿僧祇",
"那由他",
"不可思議",
"無量大数",
];
fn int2kanji(i: i64) -> String {
let s = i.to_string();
let chars = s.bytes();
let p = chars.into_iter().rev().enumerate().collect::<Vec<_>>();
let mut buf: Vec<&'static str> = Vec::new();
for (i, b) in p.clone() {
let c = (b - 48) as usize; // 48 is '0'
if i % 4 == 0
&& i > 0
&& (i..min(i + 4, s.len()))
.map(|i| {
let (_, c) = p.get(i).unwrap();
*c
})
.any(|n| n != 48)
{
buf.push(PARTS[i / 4]);
}
if c != 0 {
// その桁が 0 のときは区切りを追加しない
buf.push(SUBS[i % 4]);
}
if !(i % 4 != 0 && c == 1) {
// 十百千を表示したときで、一のときは追加しない。
buf.push(NUMS[c]); // 48 is '0'
}
}
buf.reverse();
buf.join("")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_int2kanji() {
assert_eq!(int2kanji(1), "一");
assert_eq!(int2kanji(9), "九");
assert_eq!(int2kanji(10), "十");
assert_eq!(int2kanji(11), "十一");
assert_eq!(int2kanji(21), "二十一");
assert_eq!(int2kanji(99), "九十九");
assert_eq!(int2kanji(100), "百");
assert_eq!(int2kanji(999), "九百九十九");
assert_eq!(int2kanji(1000), "千");
assert_eq!(int2kanji(9999), "九千九百九十九");
assert_eq!(int2kanji(10000), "一万");
assert_eq!(int2kanji(10020), "一万二十");
assert_eq!(int2kanji(1_000_020), "百万二十");
assert_eq!(int2kanji(100_000_020), "一億二十");
assert_eq!(int2kanji(1_0000_4423), "一億四千四百二十三");
assert_eq!(int2kanji(1_8000_4423), "一億八千万四千四百二十三");
}
}
本件は mozc の ut がどうこうとかは関係なくて、ふと linux desktop を使おうと昨年末に思いまして、昨年末からちまちまやってます
https://github.com/tokuhirom/akaza
かな漢字変換って作るの難しいのかなぁ、と思ったので作ってみている。これはまさに Just for Fun でやっている。
わりと普通に自分で常用してる分には困らないかな、というところまできている。
以下は、思ってることの垂れ流しという感じで、まとまってないですが。
「日本語入力を支える技術」という本が 2018年に出ていて、この本の内容を読めば、だいたいエンジン部分は実装できる。Amazon のレビューではこの本よんでも実装できないって書いてあるけど、変換エンジン自体は実装できます。
UI が辛い。けど。
エンジンは、ビタビアルゴリズムで最小コスト法を実装する、とかであればプログラミングできる人なら割とまじで誰でも実装できる。
ただ、どうしてもかな漢字変換エンジンは大量の細かいオブジェクトを取り扱う必要が出てくるので、python とかで実装すると結構遅い。
2年前に一回 pure python で実装したが、おそすぎたのでコア部分を C++ で書き直したという経緯がある。
Python で書いてると、色々なアルゴリズムためす上でも、Python が遅いからだめなのか、アルゴリズムがそもそも遅くて無理なのかがよくわからなくなりがちなんだよな。
UI を Python で書いて、ロジックを C++ で実装したのだがどうもインストールまわりが煩雑になるし、Rust 触ってみたかったので rust で全面的に書き直した。
Pure Rust にしたことによって、SEGV したりして謎に死ぬことがなくなったので快適になったし、高速に動いている。所有権とかがまぁめんどくさいとかはありつつ、わりと慣れれば大丈夫になる。
基本的な方針としては、mozc に近い感じの構成になっていて、最小コスト法でビタビアルゴリズムで統計的かな漢字変換である。
個人で開発しているから Google と違って大量のウェブコーパスを利用できるわけではないから、Wikipedia と青空文庫を形態素解析してそれを学習元として利用している。
mozc は Google の大量の資源を利用できるから変換精度がいいんでしょ、と思う人が多いと思うんだけど、それはまったくもってそのとおり。ただ、書き言葉という点においては、wikipedia ぐらいの規模の言語資源があれば、それを使えば割とそこそこの変換精度は出る。逆に話し言葉はめちゃくちゃ弱いので、話し言葉とかくだけた書き方のコーパスがもっとほしい。
利用できる日本語のコーパスとしては BCCWJ がほぼ唯一に近いもので、BCCWJ を使えばもっと変換精度は向上するんだけど、、個人で利用しようとすると 25万円だかかかるので二の足を踏んでいる。
識別モデルを使う というのも試してみた。今どきだと機械学習的なアプローチがナウいかなぁと思い。。だが、どうしても個人のマシンで動かしていると時間がかかってしょうがない。金をかけられない個人の趣味開発においては、統計的かな漢字変換のほうが向いていると思う。
開発を継続的にやるには無料の環境で使えるのが大事かなと思っている。モデルデータを github actions とかで生成できるのが理想的で、github actions で生成するならチープなスペックのインスタンスで 回せるぐらいのものが良い。github actions は 6 時間しか回せない。
基本的に今は単語 Bi-gram でコスト計算している。tri-gram に拡張するとプログラムが複雑になりすぎるし、有効なケースも限定的なのでまぁ。あまり bi-gram で困ってないし、という。mozc のようにクラスNグラムにしたほうがいいのかなぁ、と思いつつ、単語Nグラムで困ってないのでまぁいいかなぁといったところ。単語Nグラムだと品詞のこととか考えなくていいのがとにかく良い。
日本語の品詞難しいよ〜。個人的には、かな漢字変換の辞書登録で品詞を入れさせられるの、だるいなと前々から思ってたし。
https://komachi.hatenablog.com/entry/20081127/p2
http://www.fenix.ne.jp/~G-HAL/soft/nosettle/anthy.html#patch13
↑このへんとかを見ても思うこと。個人的には SKK の学習の具合を気に入っている。システムで提供するモデルはもちろん大事なのだが、個人の語彙などはたかが知れているので、システムで提供するモデルは「ある程度学習が進むまで耐えられる」ぐらいの精度があればよく、細かいところは個人の学習データで補ってくれや。っていうふうにするしかないのかな、と思っている。
だって個人だと Google みたいな規模のコーパス使えないんだもの。
ユーザーの学習は、unigram と bigram をテキストファイルで ~/.local/share/ とかに落とすようになっていて、自分が変換した結果の確率が学習データにあればそっちが優先されるようになっている。ので、誤変換で確定しちゃっても、正しい変換で何回か確定すればそれが優先されるようになる。このへんの学習ロジックはめちゃくちゃシンプル。基本的にユーザーの語彙は wikipedia 全体の語彙よりも少ないから、分母が小さくなるので、ユーザーの学習結果の確率計算のほうがキツく効くっていう仕組みにしている。
ちなみに、この手のかな漢字変換エンジンって、どういうふうに考えて実装したか、みたいな日記を書いてる人が多くてそういうの読むのって面白いなぁと思う。僕はそのへんの経緯を github issues とかに書くのがいいのかなぁと思ったので github issues とか discussions とか wiki に書いている。バザール的に開発できるような感じの構成にしているつもり、ではある。
お気軽になにかアイデアとかあれば書いていただければ嬉しいし、PR 送っていただければなお嬉しい。
https://github.com/tokuhirom/akaza/discussions
今月中ぐらいに一旦スナップショットリリースを出来たらいいなぁと思っている。
Rust で書かれた形態素解析機の lindera には CLI もある。
cargo install lindera-cli --features ipadic
のようにして入れる。辞書がオプショナルなので、features オプションを指定しないで入れると何もできない。ipadic か unidic を指定すること。
実行は以下のようになる。
echo "やはり原因はヤッポ" | lindera tokenize -t ipadic
やはり 副詞,一般,*,*,*,*,やはり,ヤハリ,ヤハリ
原因 名詞,一般,*,*,*,*,原因,ゲンイン,ゲンイン
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
ヤッポ UNK
EOS
https://crates.io/crates/rusty-fork
cargo test はプロセスを共有する。これではテストをしづらいケースがある。例えば C のライブラリのラッパーなどの場合、初期化関数を何回も呼んではいけないケースがある。
rusty-fork crate を利用すると、以下のように書くだけで別プロセスでテストを実行させることが可能である。
use rusty_fork::rusty_fork_test;
rusty_fork_test! {
#[test]
fn my_test() {
assert_eq!(2, 1 + 1);
}
// more tests...
}
最近、趣味で僕が書いてるプログラムでは split を 2 こまでしたいというケースが多くて、以下のように書いていた。
fn main() {
let s = "foo bar";
let p :Vec<&str> = s.splitn(2, ' ').collect();
println!("{},{}", p[0], p[1]);
}
これは、実は split_once というメソッドを使えば良い。
fn main() {
let s = "foo bar";
let (a, b) = s.split_once(' ').unwrap();
println!("{},{}", a, b);
}
便利すぎる。
https://adventures.michaelfbryan.com/posts/rust-closures-in-ffi/
↑の記事で丁寧に解説されているので、読むと良い。
ポイントとしては、rust は extern "C"
された静的な関数しか FFI 対象に渡すことができないということ。そして、closure を void*
に直接キャストすることもできないので、トランポリン使ってやりましょうね。ということです。
FFI などで *mut u8
な pointer と i32
な size が得られたときに、そこから &[u8]
というスライスにしないと rust 側では扱いづらい。
そういう場合には std::slice::from_raw_parts(image, size as usize);
を利用する。
https://stackoverflow.com/questions/50941427/casting-mut-u8-to-u8-without-std
jumpapp というコマンドがある。これを使うと該当のウィンドウが起動していなければ起動し、起動していればフォアグラウンドにもってくることができる。
僕の場合は i3wm を使っているので、.config/i3/config
に以下のように設定する。
bindsym $mod+Ctrl+Shift+o exec "jumpapp obsidian"
bindsym $mod+Ctrl+Shift+c exec "jumpapp -c google-chrome google-chrome-stable"
bindsym $mod+Ctrl+Shift+t exec "jumpapp moderndeck"
bindsym $mod+Ctrl+Shift+w exec "jumpapp wezterm"
bindsym $mod+Ctrl+Shift+f exec "jumpapp -c franz franz"
Ultimate hacking keyboard のバンパーを Ctrl+Shift+Super に割り当てて、2つのキーを押すだけでアプリを起動できるようになった。便利。
linux desktop で slack app を使おうとしてもログイン後に browser から戻ってくるところでなんかうまくいかなくてログインできないことがある。
そういう場合は、Frantz を使うといいかも。
frantz は slack や gitter などの web chat をまとめて一つの画面で表示してくれるという便利アプリ。
コレ自体便利なので昔は使っていたが最近つかってなかった。
slack のアプリと違って frantz はログインフローを frantz アプリ内の埋め込みブラウザで処理するので、slack app のような問題はおきない。
本質的な解決策ではないが、個人マシンである desktop linux でログインするのは雑談チャットとかOSS関連のチャットだけなので、まぁ frantz でいいかなぁ、といったところ。
ライフゲエム、たぶん過去に何回か作ろうと思ったことがある気がするんだが、自分が実装したものが github に見当たらなかったのでさっとつくった。もしかしたら過去にも作ったかもしれないが記憶にございません。
時間計測してないけど2時間ぐらいでできた。
https://tokuhirom.github.io/lifegame/index.html
https://github.com/tokuhirom/lifegame
react とか使ってもいいけど、こんなもんは pure javascript で書いたほうがいいだろうということで、pure javascript です。
↓このサイトを参考に実装しました(JavaScript コードは見てない)。
http://math.shinshu-u.ac.jp/~hanaki/lifegame/