
問題を出すAI
これまで作った囲碁・五目並べ・オセロのAIは、人と対局する相手でした。クロスワードは違います。AIは問題を解いたり人と競ったりせず、人が解く問題を出します。問題は韓国語のクロスワードなので、盤の1マスにはハングル1文字が入ります。
Webはほかのゲームと同じ枠組みで作りました。
- crossword.mariago.kr です。自宅の Mac の3009番ポートで動き、IDC の nginx がつないでいます。
- 待合室で難易度を選んでゲームルームに入ると、新しい盤が作られます。
- 盤と入力の一つ一つを SQLite に記録として残します。
最初の順序: 単語 → 問題 → 盤
最初は、単語バンクから単語を選び、単語ごとに問題を付けてから、それらの単語を盤の上につなげていきました。すでに置かれた単語と同じ文字があれば、その位置にかけて置く方式です。
できた盤はクロスワードではありませんでした。
- 12×12 の盤に問題が7〜8個しかありませんでした。盤のほとんどが黒マスでした。
- 答えのほとんどが2文字でした。
- 単語が最初の文字と最後の文字だけでつながり、斜めの階段のように並びました。크리스마스(クリスマス)と 알프스(アルプス)が共通の文字「스」で交わるような、真ん中の文字での交差はほとんどありませんでした。
- 単語が盤の真ん中にばかり集まりました。
- むずかしいの盤を2回続けて解いたら、一つを除いてすべて前の盤に出た単語でした。
初日に保存された24局
記録のDBには、その日に作った盤が24個残っています。すべて 12×12 のむずかしいの盤です。
- 単語の数は6〜8個、平均6.8個でした。
- 白マスは盤の平均6.5%でした。
- 答えのうち76%が2文字でした。
- 180°対称の盤は一つもありませんでした。
24局のうち22局は、1マスも埋めずにまた新しい盤を押しました。盤を見た瞬間に作り直したのです。
直そうとしたこと
- ヨコ・タテ合わせて15問以上出るようにしました。テストでは通りましたが、画面には相変わらず8問の盤が出ました。待合室が以前に作った盤を開き続けていたのです。
- 生成器を直接動かし、盤の図と問題のリストを文字で出力するテキストエージェントを作りました。記録DBの盤も一緒に検査して、2文字の島があれば印を付けます。
- 3文字の十字の塊を繰り返して盤を埋める方式も出てきました。右上の図です。問題の数は増えましたが、すべての答えが3文字で、塊同士はつながりません。いかにもAIらしい発想で、こんなクロスワードは世の中にありません。
順序を変えることに
単語から置くと、盤の形はその結果としてついてくるだけで、手を入れられません。そこで盤を先に作り、そのマスの長さに合う単語を選び、最後に問題を出すことにしました。
この段階の実際のコード
テキストエージェントが保存された盤を検査する部分です。平均の長さが短いか、真ん中の交差がなければ、新しいゲームが必要だと出力します。
scripts/text-agent.ts
for (const row of rows) {
let across = 0;
let down = 0;
let mid = 0;
let avg = 0;
try {
const puzzle = JSON.parse(row.puzzle_json) as Puzzle;
across = puzzle.across?.length ?? 0;
down = puzzle.down?.length ?? 0;
mid = countMiddleCrosses(puzzle);
avg = avgWordLength(puzzle);
} catch {
/* ignore */
}
const mark = avg >= 2.7 && mid >= 1 ? "교차있음" : "2글자섬 ← 새 게임 필요";
console.log(
`${row.code} ${row.difficulty} ${row.size}×${row.size} ${row.status} 가로 ${across} 세로 ${down} 교차 ${mid} 평균 ${avg.toFixed(2)} ${mark} ${row.created_at}`,
);単語をつなげていく配置器のルールです。新しい単語は、すでに置かれた単語とちょうど1マスだけ重なる必要があり、隣のマスに別の文字が付いてはいけません。この配置器は今も予備の経路として残っていて、3回目の記事で再び出てきます。
lib/gridFill.ts
for (const [nr, nc] of neighbors) {
if (inBoard(size, nr, nc) && grid[idx(size, nr, nc)] != null) return null;
}
}
if (intersections !== 1 || newCells < 1) return null;次の記事は「2. 盤が先、単語が次、問題は最後」です。
COMMENTS · CHAPTER 01
単語から置いたらクロスワードにならなかった コメント 0
まだコメントはありません。気になった点や補足があれば残してください。
コメントを登録するには、Googleで始めてニックネームとメールアドレスを登録してください。
Googleで始める