ゲームを作りました
エンジンの勉強として1週間でゲームを作成する Jam に参加してみました。
1週間じゃ想定していた工程の3割しか終わらないことを体感し、打ちひしがれた
今は完全に燃え尽きています、楽しかった~
あとりにゃあ
こんにちは、ねぼこです。
ととりにゃあ Advent Calendar 2021、8日目です。
毎日進捗
アトリのトトリエ
〜アーランドの錬金術士2〜
あとりにゃあ
アトリ科 Fringillidae
アトリ科(アトリか、学名 Fringillidae)は、鳥類スズメ目の科である。分類によってはアトリ亜科 Fringillinae ともなる。 アトリ(花鶏・臘子鳥・獦子鳥)と総称されるが、狭義にはその1種をアトリと呼ぶ。 南極以外の世界中に生息する。
「アトリ科」『フリー百科事典 ウィキペディア日本語版』より。
2021/06/15 20:51 UTC
URL: https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%88%E3%83%AA%E7%A7%91
日本だとそこまでメジャーな鳥ではないかも。
系統樹はどこまでをひとまとめにするかで揉めたりしていてちょっと分かりづらいけど、現在では3つの亜科に分類することが多いっぽい。
スミレフウキンチョウ亜科 Euphoniinae
昔はフウキンチョウ科だったけどアトリ科に再分類された。
スミレフウキンチョウ属とミドリフウキンチョウ属の2属からなる。
ちなみにフウキン(風琴)はオルガンのことらしい。
スミレフウキンチョウ Euphonia violacea
属名は eu-(善の) + phōnē(音) 。この由来はユーフォニウム(でっかい金管楽器)と一緒だね
鳴き声がと~っても可愛いんでしょうね 聞いたことはありません
中南米に生息している。
かわいい~
ヒワ亜科 Carduelinae
40属からなるでっかい系統。
カナリア Serinus canaria
お前アトリ科だったんか
たぶん日本ではアトリ科の中で一番有名。丸くて黄色くて小さい。愛玩動物として世界中で飼われているけど、野生種もヨーロッパ西部の離島に生息している。
鳴きまくるので毒ガスの検知に使われたりしていた。
かわいいねぇ
マヒワ Spinus spinus
ユーラシア大陸ほぼ全域に生息。日本にもいる。
冬に大陸から渡ってくるらしい。ちっちゃいのにすごいね
かわい~
ベニヒワ Acanthis flammea
こちらも大陸にからやってくる渡り鳥。顔がツルみたいね
こういう見た目が派手な鳥は大概がオスで、メスはシンプルな茶色っぽい見た目のことが多いイメージがあるけど、ベニヒワはメスも頭が赤くなっている。
かわいいね
オオマシコ Carpodacus roseus
あkkkkkkkkkkkkkっか
シベリアを中心とする亜寒帯に生息。
モフモフしててりっぱだね
かわいい
ウソ Pyrrhula pyrrhula
ウソは嘘じゃなくて口笛のこと。狂言に「嘯(うそふき)」という名前のひょっとこみたいなお面がある。
鳴き声が口笛みたいらしい。もしかして学名もオノマトペなのか!?と思って調べたけど、どうやら pyrrha(炎) が由来らしい。
かわい
アトリ亜科 Fringillinae
アトリ属のみからなる。
アトリ Fringilla montifringilla
あとりにゃあ
冬になるとシベリアから東北地方にやってくる。
漢字で書くと「花鶏」。読めるかい
特に書くことはないです
かわいいね~
おわり
恋に焦がれて 鳴く蝉よりも
— ねぼこ (@nebocco27) 2021年12月8日
鳴かぬととりが にゃあをする
整数のまま行う偏角ソート
浮動小数点数に直して $\arg$ 求めるの嫌いなので整数のままソートしましょう。 偏角の取りうる範囲は $[0, 2 \pi )$ とします。
追記
投稿直後にもっと賢い方法が投稿され、膝から崩れ落ちました
ソートする時には、二点 $p = (p _ x, p _ y), q = (q _ x, q _ y)$ が与えられたときにどちらの偏角が大きいのか判定できればOKです。 なので二点の比較を行う方法だけ考えます。
ざっくりと比較
まずは座標平面を三つに切り分けて大まかな位置を特定します。
これ以外の切り分け方でも大丈夫ですが、座標の正負だけ見ればいいのでこれが一番楽だと思います。
この段階で二点の属する領域が異なれば、それだけで大小関係を決定できます。
Python
def area(p: tuple[int, int]): x, y = p if y < 0: return 3 elif x < 0: return 2 else: return 1 def arg_cmp(p: tuple[int, int], q: tuple[int, int]): ap = area(p) aq = area(q) if ap < aq: return -1 elif ap > aq: return 1 else: return 0 # もっと詳しく比較する必要がある
Rust
use std::cmp::Ordering; fn area(p: (i64, i64)) -> u8 { let (x, y) = p; if y < 0 { 3 } else if x < 0 { 2 } else { 1 } } fn arg_cmp(p: &(i64, i64), q: &(i64, i64)) -> Ordering { let ap = area(*p); let aq = area(*q); ap.cmp(&aq) // もっと詳しく比較する必要がある }
細かな比較
座標平面上での三角形の面積の求め方 の考え方を利用すれば、面積の符号によって二点の位置関係を特定できます。 二点の偏角の差が $\pi$ 以上だと符号が反転したり変な感じになってしまうので、そうならないようにあらかじめ三つの領域に分割しました。(かしこい)
Python
def arg_cmp(p: tuple[int, int], q: tuple[int, int]): ap = area(p) aq = area(q) if ap < aq: return -1 elif ap > aq: return 1 else: px, py = p qx, qy = q z = px * qy - py * qx if z > 0: return -1 elif z < 0: return 1 else: return 0
Rust
use std::cmp::Ordering; fn area(p: (i64, i64)) -> u8 { let (x, y) = p; if y < 0 { 3 } else if x < 0 { 2 } else { 1 } } fn arg_cmp(p: &(i64, i64), q: &(i64, i64)) -> Ordering { let ap = area(*p); let aq = area(*q); ap.cmp(&aq).then((q.0 * p.1).cmp(&(p.0 * q.1))) }
あとはこれを sort
に渡せば OK です。Python では比較関数を直接渡すことはできないので、 functools
の cmp_to_key
を使いましょう。
Python
from functools import cmp_to_key points = [(0, 1), (2, -3), (-4, -5), (-6, 7)] points.sort(key=cmp_to_key(arg_cmp))
Rust
fn main() { let mut points: Vec<(i64,i64)> = vec![(0, 1), (2, -3), (-4, -5), (-6, 7)]; points.sort_by(arg_cmp); }
補足
area()
を書き換えるだけで偏角の範囲を $[- \pi / 2, \pi / 2)$ に変更したり、点 $(0, 0)$ を必ず先頭に持ってきたりできるのが嬉しいです。
使用例
ABC225E - フ
問題ページ
提出コード (Pypy3)
提出コード (Rust)
Python3 だと全然間に合いませんでした。cmp_to_key
の呼び出しが重いっぽい?
Sort Points by Argument (Library Checker)
ABC224-G Roll or Increment 雑解法
みんなはちゃんと証明しましょう
問題
リンクはこちら
解法
現在の出目が $i$ の時、出目を $T$ に変える最適な戦略でかかる合計コストを $C(i)$ と書くことにします。 戦略はゴールまで増やし続けるか一旦振り直すかです。
振り直した後にかかるコストの期待値を $r$ と置きます。すると、
$$ C(i) = \begin{cases} \min(A(T - i), B + r) & (i \le T) \\ B + r & (i > T) \end{cases} $$
です。さらに、$r = \sum C(i) / N$ です。
ここで、 $r$ をある値 $r_{a}$ だと仮定します。すると $C(i)$ が定まり、上の式を用いて $r$ が計算できます。これを $r_b$ とします。 $r_a$ が $r_b$ より大きいとき、真の $r$ は $r_a$ より小さく、 小さいときは $r_a$ より大きいはずです。 このように $r$ を二分探索することで真の $r$ および $C(i)$ が計算できます。求める答えは $C(S)$ です。
from math import ceil N, S, T, A, B = map(float, input().split()) hi = N * max(A, B) lo = 0 for _ in range(64): ra = (hi + lo) / 2. # A * (T - i) <= B + r である i の個数 count = min(T, ceil((ra + B) / A)) border = T - count # C(i) の総和 total = (ra + B) * (border + (N - T)) + A * (count - 1) * count / 2 rb = total / N if ra > rb: hi = ra else: lo = rb r = (hi + lo) / 2 if S > T: ans = r + B else: ans = min(A * (T - S), r + B) print(f"{ans:.16f}")
提出コード
雑すぎる
ABC215-E Chain Contestant 解説
文字列に含まれる文字の種類数を $σ$ とおきます。 $O(N2^{σ})$ で解きます。
問題
リンクはこちら
解法
$σ = 1$
まずは $σ = 1$ の場合を考えてみましょう。このとき答えは明らかに $2^{N} - 1$ です。これをDPで解いてみることにします。配列 $g[i] \coloneqq \text{$s[0, i)$ の範囲についての選び方}$ を考えます。例えば $s = \text{\textquotedblleft AAAAAA\textquotedblright}$ の時、 $g = [0, 1, 3, 7, 15, 31, 63]$ です。
ここで、$g[i]$ の値を求める計算を、以下のように場合分けして考えてみましょう。
最初に選ぶのが $s[0]$ のとき
$s[1], s[2], \ldots, s[i-1]$ の $i-1$ 個については、選んでも選ばなくてもよい。
$\Rightarrow 2^{i-1}$ 通り
最初に選ぶのが $s[1]$ のとき
$s[2], s[3], \ldots, s[i-1]$ の $i-2$ 個については、選んでも選ばなくてもよい。
$\Rightarrow 2^{i-2}$ 通り
$\vdots$
最初に選ぶのが $s[i-1]$ のとき
$1$ 通り
これらを足し合わせることで、確かに $\displaystyle\sum _ {j=0}^{i-1}2^{j} = 2^{i} - 1$ となり、 $g[i]$ の値と一致します。
$σ = 2$
続いて、 $σ = 2$ の場合を考えます。問題の文字列 $s$ が $\text{\textquotedblleft ABAABAB\textquotedblright}$ であるとします。 同じ種類のコンテストは必ず一かたまりで選ばなければなりませんが、この順番が $\text{A} \rightarrow \text{B}$ の場合を考えます。
まず、先ほど考えた $σ = 1$ のパターン同様、 $\text{A}$ だけを選ぶ場合を表す配列 $g _ {A}$ が存在すると考えます。これは以下のようになります。
では、改めて $g _ {\text{AB}}[i] \coloneqq \text{$s[0, i)$ から条件を満たすように A, B の順で選ぶ方法の個数}$ を埋めていきます。 $g _ {\text{AB}}[7]$ を計算するときの様子を考えてみます。
最初に選ぶ $\text{B}$ が $s[1]$ のとき
$s[0, 1)$ からいくつか $\text{A}$ を選び、$s[1]$ を選ぶ。$s[4], s[6]$ は選んでも選ばなくてもよい。
$\Rightarrow g _ {\text{A}}[2] \times 2^{2}$ 通り
最初に選ぶ $\text{B}$ が $s[4]$ のとき
$s[0, 4)$ からいくつか $\text{A}$ を選び、$s[4]$ を選ぶ。$s[6]$ は選んでも選ばなくてもよい。
$\Rightarrow g _ {\text{A}}[4] \times 2^{1}$ 通り
最初に選ぶ $\text{B}$ が $s[6]$ のとき
$s[0, 6)$ からいくつか $\text{A}$ を選び、$s[6]$ を選ぶ。
$\Rightarrow g _ {\text{A}}[6] \times 2^{0}$ 通り
これらの合計が $g _ {\text{AB}}[7]$ になります。足し合わせるべき $g _ {\text{A}}[i]$ を以下のように累積的に計算すれば、前から見ていくことで $g _ {\text{AB}}$ を計算することができます。
acc = 0 for i in range(N): if s[i] == 'B': acc = acc * 2 + gA[i] gAB[i+1] = acc else: gAB[i+1] = gAB[i]
同様に $g _ {\text{B}}$ から $g _ {\text{BA}}$ も計算できます。
ここで、 $g _ {\text{\{A,B\}}}[i] \coloneqq \text{$s[0, i)$ から条件を満たすように A, B を選ぶ方法の個数}$ という配列を考えると、 $g _ {\text{\{A,B\}}}[i] = g _ {\text{AB}}[i] + g _ {\text{BA}}[i]$ が成り立ちます。また、文字列中に出現する文字全体の集合を $U$ とおくと、本問題で求めるべき答えは
$$ \sum _ {\substack{S \subset U \\ S \neq \varnothing}} g _ {S}[n] $$
と表せます。
$σ \ge 3$ のとき
これまでとおおむね同様にして $g _ {*}$ を求めていくことができます。 例えば $g _ {\text{ABCD}}$ であれば以下のようなコードになります。
acc = 0 for i in range(N): if s[i] == 'D': acc = acc * 2 + gABC[i] gABCD[i+1] = acc else: gABCD[i+1] = gABCD[i]
特に $σ$ が大きくなると、ありうるすべての順列について $g _ {*}$ を足し合わせるのが困難となるので、直接 $g _ {S}$ を求める必要があります。この方法について、 $σ = 2$ の場合に戻って考えます。
$g _ {S}$ を直接求める
$s = \text{\textquotedblleft ABAABAB\textquotedblright}$ とします。ここで、以下の二種類の配列を考えます。
$$ \begin{aligned} f _ {\text{AB}}[i] &\coloneqq \text{$s[i-1]$ を必ず選ぶとき、 $s[0, i)$ から条件を満たすように A, B の順で選ぶ方法の個数} \\ f _ {\text{\{A,B\}}}[i] &\coloneqq \text{$s[i-1]$ を必ず選ぶとき、 $s[0, i)$ から条件を満たすように A, B を選ぶ方法の個数} \end{aligned} $$
この時、 $f _ {\text{\{A,B\}}}[i] = f _ {\text{AB}}[i] + f _ {\text{BA}}[i]$ です。また、条件を満たす選び方それぞれについて、最後の文字の位置がどこであるかを考えると、$g$ が $f$ の累積和となっていることがわかります。
さらに、 $f _ {\text{AB}}$ と $f _ {\text{BA}}$ を見比べると、一方が $0$ でないときもう一方は必ず $0$ となっています。これは、 $s[i-1]$ を必ず選ぶという条件により、$s[i-1] = \text{A}$ のときは $f _ {\text{AB}}[i] = 0$ となるためです。
$f _ {\text{AB}}$ は以下のように計算可能ですが、
fAB = [0] * (N+1) acc = 0 for i in range(N): if s[i] == 'B': # s[i-1] を必ず使うという条件により、2の指数が1減る # それに伴い更新式が若干変化した fAB[i+1] = acc + g_A_[i] acc = acc * 2 + g_A_[i]
$f _ {\text{AB}}$ と $f _ {\text{BA}}$ の更新のタイミングはかぶらないため、if
, else
によって以下のようにまとめることができます。
fAB = [0] * (N+1) fBA = [0] * (N+1) accA = 0 accB = 0 for i in range(N): if s[i] == 'A': fBA[i+1] = accA + g_B_[i] accA = accA * 2 + g_B_[i] else: fAB[i+1] = accB + g_A_[i] accB = accB * 2 + g_A_[i]
さらに配列もひとつにまとめてしまうことで、直接 $f _ {\text{\{A,B\}}}$ を計算することができます。
f_AB_= [0] * (N+1) accA = 0 accB = 0 for i in range(N): if s[i] == 'A': f_AB_[i+1] = accA + g_B_[i] accA = accA * 2 + g_B_[i] else: f_AB_[i+1] = accB + g_A_[i] accB = accB * 2 + g_A_[i]
以上の内容と、 $f$ の累積和を取ると $g$ になることから、$g _ {\text{\{A,B\}}}$ は結局以下のように直接計算することができます。
g_AB_= [0] * (N+1) accA = 0 accB = 0 for i in range(N): if s[i] == 'A': g_AB_[i+1] = accA + g_B_[i] accA = accA * 2 + g_B_[i] else: g_AB_[i+1] = accB + g_A_[i] accB = accB * 2 + g_A_[i] g_AB_[i+1] += g_AB_[i]
一般の $σ$ について
より多くの文字が含まれる場合についても、最後に選ぶ文字がどれであるかによって分類すると、同様の計算が可能です。 $\mathrm{dp}[S][i] \coloneqq \text{$s[0, i)$ から条件を満たすように、 $S$ 中の文字を選ぶ方法の個数}$ を埋めていきます。 更新式は以下のように表されます。
# 更新のイメージ for i in range(N): c = s[i] if c in S: dp[S][i+1] = acc[c] + dp[S\{c}][i] acc[c] = acc[c] * 2 + dp[S\{c}][i] dp[S][i+1] += dp[S][i]
ここで、 $\mathrm{dp}[S]$ を計算する際には $\mathrm{dp}[S \setminus \{c\}]$ が既に計算されている必要があることに注意してください。
実際に実装する際には $S$ はビット表現として持つことになるので、以下のようなコードになります。
# 各文字は適当な整数に変換 for i in range(N): c = s[i] if bit>>c & 1: dp[bit][i+1] = acc[c] + dp[bit ^ 1<<c][i] acc[c] = acc[c] * 2 + dp[bit ^ 1<<c][i] dp[bit][i+1] += dp[bit][i]
bit
について昇順に更新していけば、$\mathrm{dp}[S]$ の更新に必要な $\mathrm{dp}[T] ~ (T \subset S)$ は全て計算済みとなります。
余談
逆に言えば自分の上位集合はまだ計算されていないことになるので、if
を省略することができます。
for i in range(N): # bit & 1<<s[i] == 0 なら dp[bit | 1<<s[i]][i] == 0 # acc[s[i]] もずっと 0 のまま dp[bit][i+1] = acc[s[i]] + dp[bit ^ 1<<s[i]][i] + dp[bit][i] acc[s[i]] = acc[s[i]] * 2 + dp[bit ^ 1<<s[i]][i]
実装
全体の実装は以下の通りです。計算量は $O(N2^σ)$ です。
K = 10 MOD = 998_244_353 N = int(input()) s = [ord(x) - ord('A') for x in input()] dp = [[0] * (N+1) for _ in range(1<<K)] dp[0][0] = 1 for bit in range(1<<K): acc = [0] * K for i in range(N): dp[bit][i+1] = (acc[s[i]] + dp[bit ^ 1<<s[i]][i] + dp[bit][i]) % MOD acc[s[i]] = (acc[s[i]] * 2 + dp[bit ^ 1<<s[i]][i]) % MOD ans = sum(dp[bit][N] for bit in range(1, 1<<K)) % MOD print(ans)
ツイステのダメージ計算Webアプリを作りました
初めてのWebアプリを作りました。
プログラミングをやっているからにはいつかWebの技術も触りたいなあ、と思いながらも特に作るアイデアもなくのうのうと過ごしていましたが、ふとこれを思いついたので作ってみました。
これのためにHTML、CSS、JavaScript (Vue.js) を一から勉強しました。本当に大変だった
URLがいつまでたってもGoogleに登録されないので検索に引っかからず、だれにも使ってもらえません ウケるね いつか登録されるといいですね
作る過程で Vue.js に慣れていったので、次はもう少しうまく書ける気がします。 次は何つくろっかな~ 何かの投稿サイトとか作りたいね
Rime on Windows10
競技プログラミング作問支援ツール Rime を Windows10 で使えるように改造しましょう!
Mac なんかに……負けないっ……!
おことわり
- 作業にあたって Rime 非公式ドキュメント を大いに参考にさせていただきました。基本的にはここのチュートリアルに従い、windowsで正しく動かなかったら適宜書き換えていく、という方針で作業を行います。
- 自己責任でお願いします。そんなにパソコン全体に影響のあるような作業はしないので大丈夫だとは思いますが……
- 動かない原因をコードを読みながら延々と試行錯誤した結果なので、書き換えなくていい場所や、もっと効率のいい方法があるかもしれません。
- 「同じように作業をしたのにここが違う!」みたいなことを言われても何もわからないので自分でコードと公式リファレンス読んでください。
- Powershell を使用します。
インストール
PS C:\home> pip install git+https://github.com/icpc-jag/rime Collecting git+https://github.com/icpc-jag/rime ...(略)...
インストールが完了したことを確かめましょう。
PS C:\home> rime --help
コマンドが走らず、スクリプトファイル自体を開こうとしてしまいます。どうやらMacではファイル名を入力するだけでPythonを起動できるようなのですが、Windowsではご存じのように
python file.py
のように入力しなければなりません。ではそのようにしてみましょう。
PS C:\home> python rime --help C:\Users\Personal\AppData\Local\Programs\Python\Python39\python.exe: can't open file 'C:\home\rime': [Errno 2] No such file or directory
どうやら二番目以降の引数はパスが通っていても認識してくれないようです。まったくもう
これをフルパスで入力するとちゃんと動きます。
PS C:\home> python 'C:\Users\Personal\AppData\Local\Programs\Python\Python39\Scripts\rime' --help rime.py <command> [<options>...] [<args>...] Rime is a tool for programming contest organizers to automate usual, boring and error-prone process of problem set preparation. It supports various programming contest styles like ACM-ICPC, TopCoder, etc. by plugins. To see a brief description and available options of a command, try: rime.py help <command> Commands: ...(略)...
ただ毎回こんなことはやっていられないので、起動しやすい場所に持ってくることにします。
Rimeで作問をする専用のディレクトリを作成し、その中にさっきの場所にあるrimeとrime_initをコピーしてきましょう。エクスプローラを使ってマウスでやっても大丈夫です。(そのほうが速いかも)
PS C:\home> mkdir Rime ディレクトリ: C:\home Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2021/05/01 17:25 Rime PS C:\home> cd Rime PS C:\home\Rime> cp 'C:\Users\Personal\AppData\Local\Programs\Python\Python39\Scripts\rime' . PS C:\home\Rime> cp 'C:\Users\Personal\AppData\Local\Programs\Python\Python39\Scripts\rime_init' . PS C:\home\Rime> ls ディレクトリ: C:\home\Rime Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/05/01 17:11 172 rime -a---- 2021/05/01 17:11 2421 rime_init
こうするとほぼ本家と同じように書くことができます。
PS C:\home\Rime> py rime --help rime.py <command> [<options>...] [<args>...] Rime is a tool for programming contest organizers to automate usual, boring and error-prone process of problem set preparation. It supports various programming contest styles like ACM-ICPC, TopCoder, etc. by plugins. To see a brief description and available options of a command, try: rime.py help <command> Commands: ...(略)...
以上のように、コマンドすべての頭に py
を付ければOKです。 py
を登録していない人は python
に置き換えてください。
これでインストールは完了です。
チュートリアル
ディレクトリの初期化
PS C:\home\Rime> py rime_init --git Initialized empty Git repository in C:/home/Rime/.git/ [master (root-commit) fe093c2] Initial commit 3 files changed, 5049 insertions(+) create mode 100644 .gitignore create mode 100644 PROJECT create mode 100644 common/testlib.h
新規問題の作成
subprocess
PS C:\home\Rime> py rime add . problem aplusb Note: Running Rime under Windows will be unstable. Traceback (most recent call last): File "c:\users\personal\appdata\local\programs\python\python39\lib\site-packages\rime\core\main.py", line 144, in Main ...(中略)... FileNotFoundError: [WinError 2] 指定されたファイルが見つかりません。
出力ログを見ると、途中で call
という関数を呼んでいることが分かります。これは subprocess
というモジュールの関数なのですが、このモジュールでシェルコマンドを呼び出すためには shell=True
という引数を与える必要があります。Rime では subprocess
から call()
のほかに check_output()
と Popen()
を使っています。これらについても shell=True
を与える必要があります。
一度、Rime の中身のコードが置かれているディレクトリに移動します。
PS C:\home\Rime> cd 'C:\users\personal\appdata\local\programs\python\python39\lib\site-packages\rime' PS C:\..\..\rime>
以降、Rime 本体のコードに言及する時は基本的にこの中にあるものを指していると思ってください。スクリプト内で call()
を使っている箇所をすべて見つけるため、以下のコマンドで検索します。
PS C:\..\..\rime> findstr /s /n 'call\(' *.py plugins\plus\commands.py:63: call([EDITOR, filename])
これで 'call(' が書かれている箇所をすべて探すことができました。call()
は一度しか使われていないようです。 plugins\plus\commands.py
を見に行って、以下のように書き換えます。
def EditFile(filename, initial): EDITOR = os.environ.get('EDITOR', 'vi') files.WriteFile(initial, filename) call([EDITOR, filename], shell=True)
check_output()
, Popen()
についても同じように探して書き換えます。
comple(None, ...)
あたらめて実行しましょう。最初の実行時に作成できてしまったディレクトリは削除しておきます。
PS C:\home\Rime> py rime add . problem aplusb Note: Running Rime under Windows will be unstable. ERROR: aplusb: compile() arg 1 must be a string, bytes or AST object
これは core/targets.py
内の TargetBase.Load()
内部で、compile()
に None
を渡してしまっていることが原因です。そのため、以下のように条件分岐を付け加えておきます。
# Evaluate config. try: script = files.ReadFile(self.config_file) except IOError: raise ConfigurationError('cannot read file: %s' % self.config_file) if script is None: return try: code = compile(script, self.config_file, 'exec') self.PreLoad(ui) exec(code, self.exports, self.configs) self.PostLoad(ui) except ReloadConfiguration: raise # Passthru except Exception as e: # TODO(nya): print pretty file/lineno for debug raise ConfigurationError(e)
vim
PS C:\home\Rime> py rime add . problem aplusb Note: Running Rime under Windows will be unstable. 'vi' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。 [ ADD ] C:\home\Rime\aplusb/PROBLEM Error Summary: Total 0 errors, 0 warnings
問題ディレクトリの作成に成功すると自動で vim が起動するのですが、私は vim を入れていないので起動に失敗します。代わりに Visual Studio Code を指定します。
plugins/plus/commands.py
の EditFile()
を以下のように書き換えます。
def EditFile(filename, initial): EDITOR = os.environ.get('EDITOR', 'code') files.WriteFile(initial, filename) call([EDITOR, filename], shell=True)
お好きなエディタを指定してください。
ここまで済ませると無事に Rime/aplusb
が作成され、Rime/aplusb/PROBLEM
が自動で開きます!お疲れさまでした。まだまだ続きます。
PS C:\home\Rime> ls ディレクトリ: C:\home\Rime Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2021/05/01 18:21 aplusb d----- 2021/05/01 17:36 common -a---- 2021/05/01 17:36 72 .gitignore -a---- 2021/05/01 17:36 671 PROJECT -a---- 2021/05/01 17:11 172 rime -a---- 2021/05/01 17:11 2421 rime_init PS C:\home\Rime> ls aplusb ディレクトリ: C:\home\Rime\aplusb Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/05/01 18:21 408 PROBLEM
解答プログラムの作成
先ほど作成した
aplusb/
ディレクトリ内で、以下のコマンドを実行してみましょう。
と書かれていますが、Rime/
でもパスを指定する引数を書き換えれば同じことができるのでそうします。私は Rust が好きなので Rust で書きます。
PS C:\home\Rime> py rime add aplusb solution rust_correct Note: Running Rime under Windows will be unstable. [ ADD ] C:\home\Rime\aplusb\rust_correct/SOLUTION Error Summary: Total 0 errors, 0 warnings
Rime/aplusb/rust_correct/SOLUTION
が自動で開きます。Rust 用の行をコメントアウトして、ans.rs
を書きましょう。
fn main() { let mut io = IO::new(); input!{ from io, a: i32, b: i32 } println!("{}", a + b); }
SOLUTION
を見れば分かるように、コンパイルは rustc を通して行われるので、cargo を使ってほかのクレートを持ってくることができません。入出力関数なども自分で用意しましょう。
テスト用プログラムの作成
全部自分で書かなければならないので、generator と validator を Rust で作るのはかなり絶望的です。あきらめて C++ で書くことにします。
ここまで来たら
PS C:\home\Rime> py rime add aplusb testset tests Note: Running Rime under Windows will be unstable. [ ADD ] aplusb: C:\home\Rime\aplusb\tests/TESTSET Error Summary: Total 0 errors, 0 warnings
も問題なく実行できるはずなので、その後は本家に則って作業してください。私はコピペしました。
テストの実行
SIGXCPU
PS C:\home\Rime> py rime test aplusb Note: Running Rime under Windows will be unstable. [ COMPILE ] aplusb/tests: generator.cc ERROR: aplusb/tests: generator.cc: Compile Error (On compiling: module 'signal' has no attribute 'SIGXCPU') [ COMPILE ] aplusb/tests: validator.cc ERROR: aplusb/tests: validator.cc: Compile Error (On compiling: module 'signal' has no attribute 'SIGXCPU') Build Summary: aplusb ... in: 0B, diff: 0B, md5: d41d8cd98f00b204e9800998ecf8427e rust_correct RUST 236 lines, 5.1kB Test Summary: aplusb ... 1 solutions, 0 tests rust_correct FAIL Failed to build tests Error Summary: ERROR: aplusb/tests: generator.cc: Compile Error (On compiling: module 'signal' has no attribute 'SIGXCPU') ERROR: aplusb/tests: validator.cc: Compile Error (On compiling: module 'signal' has no attribute 'SIGXCPU') Total 2 errors, 0 warnings
python の signal というモジュールを使っているらしいのですが、SIGXCPU
という変数は存在しません。どうやら Python3.5 で SIG_DFL
という変数に統合されてしまったようです。先ほどと同様、 findstr
を使って検索してすべて書き換えましょう。
ここでは実行に時間が掛かりすぎている場合に処理を切り替えるような作業を行っていますが、コンパイルを行う際は必ず引っかかっているようです。これが正しい挙動なのかはわかりません。動けばよかろうなのです。だれか助けて~~~
seek
PS C:\home\Rime> py rime test aplusb Note: Running Rime under Windows will be unstable. [ COMPILE ] aplusb/tests: generator.cc ERROR: aplusb/tests: generator.cc: Compile Error (On compiling: 'int' object has no attribute 'seek') [ COMPILE ] aplusb/tests: validator.cc ERROR: aplusb/tests: validator.cc: Compile Error (On compiling: 'int' object has no attribute 'seek') Build Summary: aplusb ... in: 0B, diff: 0B, md5: d41d8cd98f00b204e9800998ecf8427e rust_correct RUST 236 lines, 5.1kB Test Summary: aplusb ... 1 solutions, 0 tests rust_correct FAIL Failed to build tests Error Summary: ERROR: aplusb/tests: generator.cc: Compile Error (On compiling: 'int' object has no attribute 'seek') ERROR: aplusb/tests: validator.cc: Compile Error (On compiling: 'int' object has no attribute 'seek') Total 2 errors, 0 warnings
rime/basic/codes.py
の CodeBase._ResetIO()
内で args
(stdin
, stdout
, stderr
) に対して seek()
を呼びますが、stderr
として渡される subprocess.STDOUT
はオブジェクトではなく、特殊な名前を持つただの整数です。そこで、以下のように書き換えます。
def _ResetIO(self, *args): for f in args: if f is None or f is subprocess.STDOUT: continue try: f.seek(0) f.truncate() except IOError: pass
UTF-8
PS C:\home\Rime> py rime test aplusb Note: Running Rime under Windows will be unstable. [ COMPILE ] aplusb/tests: generator.cc [ COMPILE ] aplusb/tests: validator.cc [ GENERATE ] aplusb/tests: generator.cc [ VALIDATE ] aplusb/tests: 02_random_01.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_02.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_03.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_04.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_05.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_06.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_07.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_08.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_09.in: PASSED [ VALIDATE ] aplusb/tests: 02_random_10.in: PASSED [ VALIDATE ] aplusb/tests: OK [ COMPILE ] aplusb/rust_correct [ REFRUN ] aplusb/rust_correct: 02_random_01.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_02.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_03.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_04.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_05.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_06.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_07.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_08.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_09.in: DONE [ REFRUN ] aplusb/rust_correct: 02_random_10.in: DONE [ REFRUN ] aplusb/rust_correct [ TEST ] aplusb/rust_correct ERROR: aplusb/rust_correct: 02_random_01.in: Wrong Answer judge log: C:\home\Rime\aplusb\rime-out\rust_correct\02_random_01.judge [ TEST ] aplusb/rust_correct: 02_random_01.in: Wrong Answer Build Summary: aplusb ... in: 40B, diff: 25B, md5: - rust_correct RUST 236 lines, 5.1kB Test Summary: aplusb ... 1 solutions, 10 tests rust_correct FAIL 02_random_01.in: Wrong Answer Error Summary: ERROR: aplusb/rust_correct: 02_random_01.in: Wrong Answer judge log: C:\home\Rime\aplusb\rime-out\rust_correct\02_random_01.judge Total 1 errors, 0 warnings
コンパイルはできましたが、どうやらジャッジが合わないようです。output を作成するコードと解答コードが同一なのに……どういうことでしょうか?ログに書かれている judge log を確認してみます。
'diff' �́A�����R�}���h�܂��͊O���R�}���h�A ����\�ȃv���O�����܂��̓o�b�` �t�@�C���Ƃ��ĔF������Ă��܂���B
ポンコツ太郎がよ……
Python の open()
はちゃんと文字コードを指定してあげないとこういうことが起こりがちなので、ちゃんと UTF-8 を指定してあげましょう。結構たくさんあるので頑張ってください。
def _ExecForCompile(self, args): with open(os.path.join(self.out_dir, self.log_name), 'w', encoding='utf-8') as outfile: yield (yield self._ExecInternal( args=args, cwd=self.src_dir, stdin=files.OpenNull(), stdout=outfile, stderr=subprocess.STDOUT))
こんな感じです。
diff
これでもう一度実行して judge log を見ます。
'diff' �́A�����R�}���h�܂��͊O���R�}���h�A ����\�ȃv���O�����܂��̓o�b�` �t�@�C���Ƃ��ĔF������Ă��܂���B
どうして……
私の力では judge log を出力しているのがどこなのか分からず、文字化けを治すことができませんでした。仕方がないのでここを直すのはあきらめて別エンコードで開くことにします。VSCode であれば下部の「UTF-8」と書かれた箇所をクリックすることで自分でエンコードを指定して開くことができます。
'diff' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。
subprocess
でコマンドを実行する際は Powershell ではなく cmd.exe を呼び出すのですが、残念ながら cmd.exe には diff
というコマンドはありません。代わりに fc
というコマンドがあるので、これを使えばよいです。rime/basic/codes.py
の InternalDiffCode.Run()
を書き換えます。
def Run(self, args, cwd, input, output, timeout, precise, redirect_error=False): parser = optparse.OptionParser() parser.add_option('-i', '--infile', dest='infile') parser.add_option('-d', '--difffile', dest='difffile') parser.add_option('-o', '--outfile', dest='outfile') (options, pos_args) = parser.parse_args([''] + list(args)) run_args = ('fc', options.difffile, options.outfile) # ここ with open(input, 'r', encoding='utf-8') as infile:
UTF-8 として比較するようなオプションがあり、それがないと非ASCII文字を出力する問題でバグりそうな予感がしますが、そのオプションを付けると改行が \n
か \n\r
かが区別され、正しい出力でも WA と判定されてしまうので付けないことにしました。
これで実行できるはずです。
PS C:\home\Rime> py rime test aplusb Note: Running Rime under Windows will be unstable. [ COMPILE ] aplusb/rust_correct: up-to-date [ TEST ] aplusb/rust_correct [ TEST ] aplusb/rust_correct: 02_random_01.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_02.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_03.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_04.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_05.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_06.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_07.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_08.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_09.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_10.in: PASSED [ TEST ] aplusb/rust_correct: max 0.03s, acc 0.29s Build Summary: aplusb ... in: 40B, diff: 25B, md5: - rust_correct RUST 236 lines, 5.1kB Test Summary: aplusb ... 1 solutions, 10 tests rust_correct OK max 0.03s, acc 0.29s Error Summary: Total 0 errors, 0 warnings
誤答が落ちることをテストする
せっかくなのでここも試してみます。今度はpythonで書いてみることにします。
PS C:\home\Rime> py rime add aplusb solution py_wa Note: Running Rime under Windows will be unstable. [ ADD ] C:\home\Rime\aplusb\py_wa/SOLUTION
SOLUTION
にも書かれているとおり、 shebang を忘れないようにしてください。フルパスで書かなければならず結構面倒です。ここもうまく書き換えれば shebang を省略できる気がするんですが、あんまり試してないので良く分かりません。
#!C:\Users\Personal\AppData\Local\Programs\Python\Python39\python.exe a, b = map(int, input().split()) if a % 5 == 0: a -= 1 print(a + b)
PS C:\home\Rime> py rime test aplusb Note: Running Rime under Windows will be unstable. [ TEST ] aplusb/py_wa [ TEST ] aplusb/py_wa: 02_random_01.in: PASSED [ TEST ] aplusb/py_wa: 02_random_02.in: PASSED [ TEST ] aplusb/py_wa: 02_random_03.in: PASSED [ TEST ] aplusb/py_wa: 02_random_04.in: PASSED [ TEST ] aplusb/py_wa: 02_random_05.in: PASSED [ TEST ] aplusb/py_wa: 02_random_06.in: Wrong Answer [ COMPILE ] aplusb/rust_correct: up-to-date [ TEST ] aplusb/rust_correct [ TEST ] aplusb/rust_correct: 02_random_01.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_02.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_03.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_04.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_05.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_06.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_07.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_08.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_09.in: PASSED [ TEST ] aplusb/rust_correct: 02_random_10.in: PASSED [ TEST ] aplusb/rust_correct: max 0.02s, acc 0.20s Build Summary: aplusb ... in: 40B, diff: 25B, md5: - rust_correct RUST 236 lines, 5.1kB py_wa SCRIPT 6 lines, 142B Test Summary: aplusb ... 2 solutions, 10 tests rust_correct OK max 0.02s, acc 0.20s py_wa OK 02_random_06.in: Wrong Answer Error Summary: Total 0 errors, 0 warnings
しっかり WA も判定できました
やった~~~~~~~!!!!!!!!!!!!!!!!!!!!!!
おわりに
とてもがんばりました。
よき作問ライフを!