SATySFiでテストをするためのパッケージ
概要
自分が作成したassert-eqというパッケージを使用することで、関数のテストが簡単に行えます。
インストール方法
使用しているプリミティブの都合で、SATySFi v0.0.4以降でないと行けません。
Satyrographosを使う場合
$ opam update $ opam install satysfi-assert-eq $ satyrographos install
でインストールができます。
手動で行う場合
まず
$ git clone https://github.com/puripuri2100/SATySFi-assert-eq.git
をすることで手元にリポジトリのコピーを作成します。
次に、LIBROOT/local/packages/
以下にassert-eq
というフォルダを作ります。
最後に、フォルダ内のassert-eq.satyg
をLIBROOT/local/packages/assert-eq/
にコピーします。
これで完了です。
提供する関数
モジュール名はAssertEq
です。
assert-eq : string?-> ('a -> string) -> 'a -> 'a -> unit
\assert-eq : [string?; ('a -> string);'a; 'a] inline-cmd
+assert-eq : [string?; ('a -> string);'a; 'a] block-cmd
組版時には\assert-eq
コマンドはinline-nil
と同じ挙動をし、+assert-eq
はblock-nil
と同じ挙動をします。
assert-eq <function> <left> <right>
で<left>
と<right>
が一致すると標準出力でログが、一致しないとエラー報告が出ます。
<function>
は<left>
や<right>
での値を文字列に変換するための関数です。
例えば
assert-eq arabic (1+2) 3
ではログが、
assert-eq arabic (1+2) 4
ではエラーが出ます。
ログを見るときにわかりやすいようにラベルを付けることができます。
assert-eq ?:(`label`) arabic (1+2) 3
のようにして使えます(\assert-eq
と+assert-eq
でも同様にしてに使えます)。
具体的な使い方
先日記事を書いたdebug-show-valueと組み合わせることで簡単にテストできることを期待しています。
例えば、num-conversionでは
@require: stdjabook @import: ../num-conversion @require: assert-eq/assert-eq @require: debug-show-value/debug-show-value open NumConversion open DebugShowValue in document (| title = {変換テスト}; author = {\@puripuri2100}; show-title = false; show-toc = false; |) '< +assert-eq(show-opt show-string)(to-zenkaku 1230)(Some(`1230`)); +assert-eq(show-opt show-int)(from-zenkaku `1230`)(Some(1230)); +assert-eq(show-opt show-string)(to-zenkaku (-100))(None); +assert-eq(show-opt show-int)(from-zenkaku `1230`)(None); +assert-eq(show-opt show-string)(to-roman-upper 0) (None); +assert-eq(show-opt show-string)(to-roman-upper 1) (Some(`I`)); +assert-eq(show-opt show-string)(to-roman-upper 2) (Some(`II`)); +assert-eq(show-opt show-string)(to-roman-upper 3) (Some(`III`)); +assert-eq(show-opt show-string)(to-roman-upper 4) (Some(`IV`)); +assert-eq(show-opt show-string)(to-roman-upper 5) (Some(`V`)); +assert-eq(show-opt show-string)(to-roman-upper 6) (Some(`VI`)); +assert-eq(show-opt show-string)(to-roman-upper 7) (Some(`VII`)); +assert-eq(show-opt show-string)(to-roman-upper 8) (Some(`VIII`)); +assert-eq(show-opt show-string)(to-roman-upper 9) (Some(`IX`)); +assert-eq(show-opt show-string)(to-roman-upper 10) (Some(`X`)); +assert-eq(show-opt show-string)(to-roman-upper 11) (Some(`XI`)); +assert-eq(show-opt show-string)(to-roman-upper 14) (Some(`XIV`)); +assert-eq(show-opt show-string)(to-roman-upper 100) (Some(`C`)); +assert-eq(show-opt show-string)(to-roman-upper 4999)(Some(`MMMMCMXCIX`)); +assert-eq(show-opt show-string)(to-roman-upper 5000)(None); +assert-eq(show-opt show-string)(to-roman-lower 0) (None); +assert-eq(show-opt show-string)(to-roman-lower 1) (Some(`i`)); +assert-eq(show-opt show-string)(to-roman-lower 2) (Some(`ii`)); +assert-eq(show-opt show-string)(to-roman-lower 3) (Some(`iii`)); +assert-eq(show-opt show-string)(to-roman-lower 4) (Some(`iv`)); +assert-eq(show-opt show-string)(to-roman-lower 5) (Some(`v`)); +assert-eq(show-opt show-string)(to-roman-lower 6) (Some(`vi`)); +assert-eq(show-opt show-string)(to-roman-lower 7) (Some(`vii`)); +assert-eq(show-opt show-string)(to-roman-lower 8) (Some(`viii`)); +assert-eq(show-opt show-string)(to-roman-lower 9) (Some(`ix`)); +assert-eq(show-opt show-string)(to-roman-lower 10) (Some(`x`)); +assert-eq(show-opt show-string)(to-roman-lower 11) (Some(`xi`)); +assert-eq(show-opt show-string)(to-roman-lower 14) (Some(`xiv`)); +assert-eq(show-opt show-string)(to-roman-lower 100) (Some(`c`)); +assert-eq(show-opt show-string)(to-roman-lower 4999)(Some(`mmmmcmxcix`)); +assert-eq(show-opt show-string)(to-roman-lower 5000)(None); +assert-eq(show-opt show-int)(from-roman-upper `I`) (Some(1)); +assert-eq(show-opt show-int)(from-roman-upper `II`) (Some(2)); +assert-eq(show-opt show-int)(from-roman-upper `III`) (Some(3)); +assert-eq(show-opt show-int)(from-roman-upper `IV`) (Some(4)); +assert-eq(show-opt show-int)(from-roman-upper `IIII`) (Some(4)); +assert-eq(show-opt show-int)(from-roman-upper `V`) (Some(5)); +assert-eq(show-opt show-int)(from-roman-upper `VI`) (Some(6)); +assert-eq(show-opt show-int)(from-roman-upper `VII`) (Some(7)); +assert-eq(show-opt show-int)(from-roman-upper `VIII`) (Some(8)); +assert-eq(show-opt show-int)(from-roman-upper `IX`) (Some(9)); +assert-eq(show-opt show-int)(from-roman-upper `VIIII`) (Some(9)); +assert-eq(show-opt show-int)(from-roman-upper `X`) (Some(10)); +assert-eq(show-opt show-int)(from-roman-upper `XI`) (Some(11)); +assert-eq(show-opt show-int)(from-roman-upper `XIV`) (Some(14)); +assert-eq(show-opt show-int)(from-roman-upper `C`) (Some(100)); +assert-eq(show-opt show-int)(from-roman-upper `MMMMCMXCIX`)(Some(4999)); +assert-eq(show-opt show-int)(from-roman-lower `i`) (Some(1)); +assert-eq(show-opt show-int)(from-roman-lower `ii`) (Some(2)); +assert-eq(show-opt show-int)(from-roman-lower `iii`) (Some(3)); +assert-eq(show-opt show-int)(from-roman-lower `iv`) (Some(4)); +assert-eq(show-opt show-int)(from-roman-lower `iiii`) (Some(4)); +assert-eq(show-opt show-int)(from-roman-lower `v`) (Some(5)); +assert-eq(show-opt show-int)(from-roman-lower `vi`) (Some(6)); +assert-eq(show-opt show-int)(from-roman-lower `vii`) (Some(7)); +assert-eq(show-opt show-int)(from-roman-lower `viii`) (Some(8)); +assert-eq(show-opt show-int)(from-roman-lower `ix`) (Some(9)); +assert-eq(show-opt show-int)(from-roman-lower `viiii`) (Some(9)); +assert-eq(show-opt show-int)(from-roman-lower `x`) (Some(10)); +assert-eq(show-opt show-int)(from-roman-lower `xi`) (Some(11)); +assert-eq(show-opt show-int)(from-roman-lower `xiv`) (Some(14)); +assert-eq(show-opt show-int)(from-roman-lower `c`) (Some(100)); +assert-eq(show-opt show-int)(from-roman-lower `mmmmcmxcix`)(Some(4999)); +assert-eq(show-opt show-string)(to-kansujix 100)(Some(`一〇〇`)); +assert-eq(show-opt show-int)(from-kansujix `一〇〇`)(Some(100)); +assert-eq(show-opt show-string)(to-kansujix (-100))(None); +assert-eq(show-opt show-int)(from-kansujix `1〇〇`)(None); +assert-eq(show-opt show-int)(from-kansujix `百`)(None); >
のようにしてテストファイルを書き、コンパイルをしてエラーが出ないかを確かめています。
おわりに
これでようやくテストを回せるようになりました。
使いこなしていきたいものです。
SATySFiでlistやoptionなどを文字列化して表示する方法
概要
自分が作成したdebug-show-valueというパッケージを使用することで、リストやオプションなどを簡単に文字列化できます。
自分の作った関数のデバッグ用にとても便利です。
インストール方法
Satyrographosを使う場合
$ opam update $ opam install satysfi-debug-show-value $ satyrographos install
でインストールができます。
手動で行う場合
まず
$ git clone https://github.com/puripuri2100/SATySFi-debug-show-value
をすることで手元にリポジトリのコピーを作成します。
次に、LIBROOT/local/packages/
以下にdebug-show-value
というフォルダを作ります。
最後に、フォルダ内のdebug-show-value.satyg
をLIBROOT/local/packages/debug-show-value/
にコピーします。
これで完了です。
提供する関数
モジュール名はDebugShowValue
です。
プリミティブな型の変換
show-int : int -> string
show-string : string -> string
show-bool : bool -> string
show-length-pt : length -> string
show-length-mm : length -> string
show-length-inch : length -> string
show-unit : unit -> string
show-point : point -> string
show-color : color -> string
関数名から役割はわかるとは思いますが、その型を与えると文字列にします。length
に関しては、基準とする単位を定めることができます。
組み合わせる型の変換
show-tuple : ('a -> string) -> 'a list -> string
show-list : ('a -> string) -> 'a list -> string
show-opt : ('a -> string) -> 'a option -> string
show-opt-list : ('a -> string) -> ('a option) list -> string
show-tuple
では同じ型のtupleしか表現できないので、異なる型でのtupleを表示したい場合は、
show-tuple (fun s -> s) [show-int 1; show-bool true]
などのようにしてください。
おわりに
リストの表示やoption型の表示は結構面倒なので、これで簡略化できるととても良いと思っています。
SATySFiでローマ数字・漢数字に変換する方法
概要
自分が作成したnum-conversionというパッケージを使用することで、ローマ数字変換・漢数字変換を楽に行うことができます。
インストール方法
注意事項
このパッケージではstring-explode
というプリミティブを使うので、2020/2/22以降のHEAD版が必要です(将来v0.0.5としてリリースされる予定のものです)。
Satyrographosを使う場合
$ opam update $ opam install satysfi-num-conversion $ satyrographos install
でインストールができます。
手動で行う場合
まず
$ git clone https://github.com/puripuri2100/SATySFi-num-conversion.git
をすることで手元にリポジトリのコピーを作成します。
次に、LIBROOT/local/packages/
以下にnum-conversion
というフォルダを作ります。
最後に、フォルダ内のnum-conversion.satyh
をLIBROOT/local/packages/num-conversion
にコピーします。
これで完了です。
提供する関数
モジュール名はNumConversion
です。
ローマ数字関連
to-roman-upper : int -> string option
to-roman-lower : int -> string option
どちらも、数字を与え、その数字が変換可能なものであった場合にローマ数字となる文字列を返します。 0より大きく5000より小さい整数が変換できます。
違いは、返ってくる文字列のアルファベットが大文字であるか小文字であるかだけです。
from-roman-upper : string -> int option
from-roman-lower : string -> int option
どちらも、文字列を与え、その文字列がローマ数字として解釈可能なものであった場合に整数に変換します。
漢数字関連
to-kansujix : int -> string option
数字を与え、その数字が変換可能なものであった場合に漢数字となる文字列を返します。
0以上の整数が変換可能です。
この漢数字列は算用数字の表記をそのまま漢数字に当てはめた、俗に一〇方式と呼ばれる方式の表記になります。
位も書く一般的な十方式はまだ実装していません。
from-kansujix string -> int option
一〇方式で表記された文字列を受け取ると整数に変換します。
全角数字関連
to-zenkaku : int -> string option
数字を与え、その数字が変換可能なものであった場合に全角数字となる文字列を返します。
0以上の整数が変換可能です。
from-zenkaku string -> int option
全角数字の文字列を受け取ると整数に変換します。
おわりに
ローマ数字あたりは使いどころがかなりありそうだと思っています。早く十方式の漢数字表記も実装したいと思っています。
使っていただけると嬉しいです。
絵文字結合を実装する
先日、SATySFiで絵文字の結合を実装したので、そこで得た知見をまとめたいと思います。
絵文字結合とは
世の中に絵文字はたくさんありますが、それだけでは飽き足らなかった人類は「絵文字を合字みたいに結合させて新しいものを作ってしまえばよくない?」と思いつきます。Unicodeポイントを浪費してはいけないという理性が無駄に働いてしまったのでしょうね。
具体的には国旗・肌の色・性別・職業などです。
大きく分けて、2つの特定の絵文字が並んだときに結合させる種類と、特定の文字でもって結合させる種類の2つが存在し、それぞれ対応しなければいけません。
実装の方針
前提:
- 入力は複数の文字列の形で与えられる
- 文字列を分解・結合させてIDを作り、事前に用意してあったグラフィックのリストの中からIDを使って適切なグラフィックを探す
- 出力はグラフィック列
- 適切に解析できれば意図している絵文字に必ずなる文字列が与えられている。つまり、結合できないのに結合できると勘違いして文字列があたえられることはないということ
特定の絵文字2つで結合させる
特定の絵文字2つで結合する規則を持っているのは
- 国旗
- 肌の色
の2つです。
国旗結合
国名のアルファベット2文字が並ぶと国旗になります。 例えば日本の国旗であれば、Jの絵文字(U+1F1EF)とPの絵文字(U+1F1F5)が並んだ時に成立します。 間に何らかの文字が入れば成立しません。
この機能の実装は
- 1文字目がアルファベットの絵文字かを判定します
- 1でそうだとわかれば2文字目もアルファベットの絵文字かを判定します
- 1文字目も2文字目もアルファベットの絵文字でなければ分割したまま終える
- 両方ともアルファベットの絵文字であれば、国旗の組み合わせとなる2つの絵文字であるかを判定し、そうであればペアとして扱い、そうでなければ分割したまま終える
という方法で行います。国旗の個数は200弱と多いですが、法則も何もないのであらかじめペアとなる2つの文字の組を用意しておかないといけません。
肌色の変更
肌の色の変更は"人に関連する絵文字"+"特定の色の絵文字"によって実現します。 なので、グラフィックが用意されている「人に関連する絵文字」のリストを用意しておき、1個目の絵文字が人に関連するもので、2個目がU+1F3FBからU+1F3FFの間に入っているものであれば、その2つを結合させるだけで終わります。
地域の旗の結合(おまけ)
実は国旗以外にも、結合する旗の絵文字があります。 "黒旗(U+1F3F5)" + "0-9-a-zのいずれか5文字" + "Cancel TagU+E007F" の列でウェールズなどの旗にすることができます。よくわからないですね。 現在のところ
の7つが登録されているので、黒旗を見つけたらとりあえず後ろの文字を読んでパターンマッチで該当すれば結合、していなければ分割で実装します。
ZWJを使った結合
ZWJ(U+200D)という文字を間に入れることで絵文字同士を結合させます。たとえば黒旗(U+1F3F4)とZWJ(U+200D)と髑髏(U+2620)の3つが並ぶと海賊旗になります。
結合の組み合わせも莫大な上、規則性も存在しないので「ZWJがあったらその前後の絵文字は結合させることができる」と決め打ちして実装します。
実装そのものは単純で、1文字目・2文字目・3文字目を常に見て、2文字目がZWJであれば1文字目と3文字目を結合させるだけです。
結合の組み合わせ
「特定の2文字で結合」と「ZWJを使って結合」は同時に起こります。 例えば"U+1F469 U+1F3FD U+200D U+1F91D U+200D U1F468 U+1F3FF"という文字列は「medium skin toneの肌色の女の人とdark skin toneの肌色の男の人」が手をつないでいる絵文字になります。(U+1F91Dは🤝という絵文字)
よって、結合の順序は
- まず特定の2文字で結合する絵文字について結合処理を行い、結合できる絵文字についてはリストにしてまとめる
- 次にZWJを間に持つ2つの文字のリストを探し、あればそれらをまとめる
という順序になります。最初にZWJで結合させようとすると、肌色や国旗の結合に対応できません。
U+FE0Fの存在
絵文字であることを明確にするためにU+FE0Fという文字を末尾に付けることがあります。気を付けましょう。
グラフィックが無かったら
グラフィックが見つからなかった場合、結合してある絵文字をもう一度分解し、それぞれに対してグラフィックを割り当てて表示しましょう。対応するグラフィックが無い場合の処理についてはここに書いてあります。
おわりに
絵文字の仕様よくわからん
SATySFiで文字列を分割する方法の速さを調べてみた
はじめに
SATySFi v0.0.4でstring-explode: string -> int list
というプリミティブが追加されました。
これは文字列をユニコードポイントのリストにするもので、これによって文字列の分割や比較が楽になりました。
文字列を一文字ずつのリストにすることは結構ありますが、今まではstring-sub
を再帰関数を使って分割していましたが、この方法と比べてstring-explode
を使う方法はどれくらい早いのか気になったので調べてみました。
実装はstring-explode
を使う方が簡単なのですが、遅かったら使いにくいので検討してみる価値はあると思います。
実験してみた
実験方法
実装はこのようになっています。
string-sub
を使った実装
let str-to-lst-sub str = let str-len = string-length str in let-rec sub lst n = if n <= 0 then lst else let s = string-sub str (n - 1) 1 in sub (s::lst) (n - 1) in sub [] str-len
string-explode
を使った実装
let str-to-lst-explode str = str |> string-explode |> List.map (fun i -> string-unexplode [i])
- テスト用文書として「吾輩は猫である」の冒頭を分割し再度文字列化するものを2000回繰り返した。A4サイズで1177ページ程度。
- Windows 10 + Ubuntu on WSL + SATySFI v0.0.4 で比較した。
- 3 回予備で実行した後、9 回実行して所要時間を計測、その中間にある 5 回分の平均値を求めた。
実験結果
string-sub
を使ったものは24.000秒string-explode
を使ったものは24.507秒
string-explode
を使った方はstring-sub
を使った方に比べて1.02倍時間がかかる
考察
ほとんど時間が変わらない。string-explode
を使った結果実装が簡単になるならこちらを採用するのも良いかもしれない。
しかし、v0.0.4からなのでそこは気を付けないといけないかもしれない。
TeXConf 2019の供養
この記事はTeX & LaTeX Advent Calendar 2019の22日目の記事で、SATySFi Advent Calendar 2019の22日目の記事です
TeX & LaTeX Advent Calendar 2019の21日目はToshioCPさんで23日目はwtsnjpさんです。
SATySFi Advent Calendar 2019の21日目はmonaqaさんで23日目はbd_gfngfnさんです。
台風によって残念ながら中止となってしまったTeXConf 2019の発表で使う予定だったスライドを供養します。
SATySFiのテキストモードを使ってtexファイルを生成する話をする予定でした。
このスライドで紹介したパッケージはGitHubのSATySFi-textmode-latex-packagesというリポジトリに置いてあります。
ペンパ描画ソフトウェアの構想(実装)
これは「ペンシルパズルA Advent Calendar 2019」の5日目の記事です。
4日目はpzdcさんでした。6日目はnyoroppyiさんです。
はじめに
普段はパズルと解いたりたまに作ったりしているpuripuri2100です。こんにちは。 最近では「ドッスンフワリ35*70早解き大会」を主催していました。
他には開成高校のパズル研究部の部長と編集長をしています。 編集長は3年目になります(技術継承しないと……)。
さて、世の中には数えきれないほど多種多様なペンシルパズルがあります。 ニコリに体裁されているパズルであれば「ぱずぷれ」でほとんど画像化と共有ができますし、「penpa-editor(ペンパくん)」を使えばオリジナルパズルだって画像化と共有ができてしまいます。
しかし、この二つのソフトウェアはGUIのWebソフトウェアであり実装もどちらもJava Scriptです。 ペンシルパズル描画ソフトウェアにはもっと多様性があっても良いのではないでしょうか?
そういうわけで今回「リスト構造・代数的データ型・パターンマッチ」等のある程度の機能が備わっている一般的なプログラミング言語であればペンシルパズルを描画できる方法を考案し、実際にSATySFiというPDFを出力するための言語で実装してみました。この方法の一番重要なのは「パズルを全て文字だけでかけるようする」という点です。
今回実装したものはここにおいてあります(メインはこの中のpuzzle.satyh
というファイルです)ので、コードを眺めながらこの記事を読んでいただけるとわかりやすいかもしれません。
ここから先、プログラミング要素がかなり多くなります。よくわからない時は「そういうものなのか~」程度でのんびり読み進めていただけると嬉しいです。
何を描画するか
一口にペンシルパズルと言っても、ヤジリンのようにマスを塗って線を引くものから数独の用に数字を埋めるもの、ハニーアイランドのように6角形のマスのものまで様々です。
この中で、今回は四角いマスを持ちその中で線を引いたり升目を塗ったり丸を描いたり数字や文字を入れたりするパズルを描画できるようにしていきたいと思います。
理由として以下のことが挙げられます。 - マス内に入れるものが数字でも記号でも文字でも労力はあまり変わらない - 線を引けないとかなり多くのメジャーなパズルが描画できなくなるので対応できるようにしたい - 四角形ではないマスを使うパズルはかなり種類が少なく、対応する労力に見合うほどではない
描画する記号類
これに関しては使用する言語がどこまで表現できるかにかかっていますが、SATySFiで実装するものに関しては、ベジェ曲線などを使って描けるものなら何でも描けるようにします。
描画できる文字
こちらも使用する言語とPDFライブラリにかかっていますが、最低でも基本的なアルファベット程度は使えるようにしたいですね。
構想
さて、今回の記事のメインです。
ここではどんな風にすれば大体のペンパを描画できるかを考えていきます。
セルの種類
盤面にはよく見るとセルがたくさんありますが、これらを
- 記号等が入って盤面を構成するセル
- 記号等が入ってパズルを構成するものだが、盤面の外に配置されるセル
- 何も記号が入らず、パズルを構成しない“空白”となるセル
の3つに分類します。
ここでは便宜的にそれぞれInSideCell
・OutSideCell
・NullCell
と名づけます。
OutSideCell
の代表的な使い道としてはキンコンカンでのアルファベットを書く部分でしょうか。
盤面を書く
さて、折角セルの種類を分けてみたので盤面を簡単に作ってみます。 中身の記号類は一旦放置して種類だけでも並べてみましょう。
例えば、このようなぬりみさきは
IIIIII IIIIII IIIIII IIIIII IIIIII IIIIII
のように書けますし、
のようなビルディングは
NNNO OIII NIII NIII
のように書けます。結構簡単ではないでしょうか?
本当はここに書いてあるI
(InSideCell
の略です)やO
(OutSideCell
の略です)に、書いてある数字や白丸についての情報が入るわけですが、すくなくともこれだけで盤面は作れてしまいます。
セルの分け目の線を引く
「セルの分け目の線」って何?と思った人も多いと思います。 簡単に言えば の太線と細線です。
太線は「盤面の内側と外側との境界」ですので、InSideCell
とOutSideCell
が接している部分か、InSideCell
とNullCell
が接している部分と言い換えることができます。
細線は「盤面内のセル同士の境界」なのでInSideCell
とInSideCell
が接している部分となります。
実際に実装する際にはこのように横と縦に一つずつ見ていって隣あう二つのセルの種類で判定をします。この図で青丸のところが外側の境界線となる箇所で、赤丸が内側同士の境界線となる箇所です。
どの座標の境界線がどの種類の境界になるかを判定したらそれぞれデータとして保存しておきます。そして、このデータと、座標を元に線を引く関数を使ってあとで一括で盤面を描画します
さて、この方法を使うと複雑な形の盤面であっても対応することができます(当然ではありますが)のでドーナツ型の盤面を作りたい時などにも使えます。
置く記号の指定
一口に記号と言ってもいくつもの種類があるので、特徴ごとに分けて記号を指定して行きたいと思います。
まずは文字です。
String(セルの中での位置, 文字列)
などのように書けると良いでしょう。
次に黒丸や黒マスなどの記号です。
Object(セルの中での位置, 記号本体の描画)
で行けると思います。ただ、「記号本体の描画」はそれぞれのプログラミング言語とライブラリに強く依存します。
次に線です。
Line(太さなどの線の引き方, 始点の位置, 次の点のセルの相対位置とその中での位置)
です。かなり冗長ですが、できなくはないと思います。
書き方はこれで良いのですが、一つのセルの中に記号が複数入る事はあるので念のためリスト構造にしておきましょう(リスト構造とは、同じ種類のデータを並べる時につかうもので、[1, 2, 3, 4]
などのように書きます)。
さて、これで盤面はいつでも作れるようになりました。 ためしに
を文字で書いてみましょう(出てくる関数や記法は架空のものです)。
InSideCell
はI
に、OutSideCell
はO
に、NullCell
はN
にしています。
[ [I(), I(), I(), I(), I(), I([Object(0.5, 0.5, cycle-num(6))])], [I([Object(0.5, 0.5, cycle-num(2))]), I(), I(), I(), I(), I()], [I(), I(), I([Object(0.5, 0.5, cycle-num(4))]), I(), I(), I()], [I(), I([Object(0.5, 0.5, cycle]), I(), I(), I(), I()], [I(), I(), I(), I(), I([Object(0.5, 0.5, cycle]), I()], [I(), I([Object(0.5, 0.5, cycle-num(3))]), I(), I([Object(0.5, 0.5, cycle]), I(), I()], ]
やや面倒ですが、結構書けますね。 もっと簡単にしたかったら
[ {||||||\cycle{6}|}; {|\cycle{2}||||||}; {|||\cycle{4}||||}; {||\cycle{}|||||}; {|||||\cycle{}||}; {||\cycle{3}||\cycle{}|||}; ]
のような記法から最初のデータを生成するなどすればかなり楽になります。
実装
実装についても書く予定でしたが、間に合いませんでしたすみません……。 追記するかもしれません。
実装ではさらにいくつかの工夫を施していますが、そこまで重要ではないでしょう。
何はともあれオリジナルパズルを文字だけで描けるようになったのは画期的だと思います(個人的に)。