Tokyo Westerns CTF 3rd 2017 Writeup
https://tokyowesterns.github.io/ctf2017/ja/
2, 3 回目くらいの CTF。
Ofuna Gourmet Explorers で参加して 28/901 位でした。
ほとんどPPCしかやってないものの、そこそこ高得点な問題も解けたりFA取れたりしたのでよかったです。
ICPC 2014 Taichung Regional Contest 参加記
12問 (A-L) のうち8問 (ABDEFGIJ) を解いて大学別4位(チーム別8位くらい?)でした。
上から上海交通・国立台湾・京都・筑波らしいのでさすがにいくらかはスロット確保できてるはず。
ICPC 2014 Tokyo Regional Contest 参加記
7完でチーム順位6位(大学順位5位)でした。上のチームに中国チームが多いのでまた世界大会参加できるかも?
基本戦略は予選と同様にzerokugi君とn_vip君に読解・解法を丸投げして私が実装するという流れでした。
WFが狙える順位をとるという目標はとりあえず達成できたような気がするものの、Colorful Jumbo Chickenに僅差で負けてしまったのがかなり悔しいので台湾でリベンジしたいですね。
ICFP Programming Contest 2014
今年も会社の人と合宿で参加、チームメンバーは @foota, @logicmachine, @nabesan_tofu, @tanakmura, @tomerun の5人でした。
提出物: https://onedrive.live.com/redir.aspx?cid=ecb59e566c2d71f1&page=self&resid=ECB59E566C2D71F1!4174&parId=ECB59E566C2D71F1!3550&authkey=!Alp6E3GNPHKNM7k&Bpub=SDX.SkyDrive&Bsrc=Share
tanakmuraさんの参加記: http://d.hatena.ne.jp/w_o/20140728#1406555463
やったこと
基本的にLambda-ManのAIに必要なものばかりやっていました。アセンブラでライブラリ書いたりAI実装したり。
Lambda ManのAI
小さい盤面用・大きい盤面用とゴーストが接近しているとき用の3種類のAIをくっつけてあります。
小さい盤面では、毎ターン最寄の点数源を持つセルへの最短路を求めてその方向へ移動します。あとPower Pillをとった後はゴーストの方に近づこうとしてみたり。最短路を求めるのにはダイクストラ法を使っています。(重みづけできたりBFSでもlog nが消せなかったりするため。)
大きい盤面では毎ターン経路探索をするとCycleLimitに引っかかるので事前に求めた経路をたどるだけにしています。初期化時は割と十分な時間があるので最初に幅優先探索(実装はダイクストラ法)をしてその結果のBFS-Treeを深さ優先でたどっていくことで全セルを通る経路を生成しています。実際にはゴーストなどの影響による中断・再開を考慮するため、各セルに4方向の部分木の処理が終わったかの情報も持たせて、適宜それを参照して再開します。あとは自明な最適化として得点源を持たないサブツリーはまとめて枝刈りしたり。一応、妨害が少なければ制限時間内に256x256でも辿れるようにはなっているはず。
ゴースト避けAIでは4近傍のセルについて、各セルからすべてのゴーストより先に到達可能なセルの数をある程度の範囲内で求め、その数が最大のセルに移動するようになっています。セルの周囲の壁の数やアイテムなどで重みをつけてみたりしたものの、結局最後まであんまりいい感じにはならず……
ライブラリ
クリティカルなところはアセンブラを書いてライブラリ化。一応そこそこ高速なものは作れたつもり。
最終版で使ってたライブラリは以下のような感じ。
時系列
きちんと文章でまとめるのめんどくさいので箇条書きで。
あんまり他の人がやってることに首突っ込めなかったんで自分がやってた作業中心に。
1日目
- 問題読む。長い。けど半分くらいは謎アーキテクチャの説明に見える。
- どうやら先のアーキテクチャ用のパックマンAIを作ればいいらしい。とりあえず簡易アセンブラを書いてシミュレータで遊んでみる。
- carとかcdrとかLispっぽいけどLispわからない……
- 破壊的代入とかできないっぽい
- スタックマシンなら昔ActionScriptのVMいじってた時のあれこれが使えるかと思ったけどなんか思っていたのと違う
- むしろ関数型な思想で書いた方がよさそう
- Environment Frameとかいまいち理解できなかったけど何となく手でもある程度は書けそうな気はする
- なんかゴースト用CPUの仕様もある。どうせLightning終わってから書かされるんだろうと思いつつ読み飛ばす。
- とりあえず最寄りの点数源へ向かう処理(→幅優先探索)を書こうとする
- そのための下地になるライブラリを書いていく(よく使いそうな関数とか配列とか)
- 日が昇りそうになってきたあたりでとりあえず配列まで書けたので寝る
2日目
- 朝食時に昨晩やったことを伝えて二度寝。結局再開は昼から
- 二度寝している間にGCC-Cコンパイラの初期版が出来上がっていた
- とりあえずLightningまでにそこそこの点数をとれるように幅優先探索を書く
- かなり遅いけどとりあえずそれっぽい動作をするようになる
- よくGhostにぶつかって死ぬのでなんとなくゴーストの周りを避けるように修正
- パラメータによってはサンプルくらいはクリアできるようになる
- ここまでで一旦提出
- 案の定「ゴーストも書いて提出してね!」
- 現状まだできてなくてやったほうがよさそうなもの
- 大きい盤面をどうにかしてたどれるようにする
- ゴースト対策があんまりにも雑なので改善する
- 強いゴーストを書く
3日目
- どうせ配列アクセスがO(log n)なせいで幅優先探索がO(|E| log |V|)みたいになってしまっているからとダイクストラ法を書いてみる
- 重みづけとかできるといろいろ便利そう
- 結果的にそこそこ高速に動作したので採用
- ライブラリの再調整
- Power Pill 取得時にゴーストを叩きに行くようにする
- どうやらゴーストがもりもり強くなっているらしい
- デバッグの様子を覗いてみると確かに瞬殺されてしまっている……
- 大きいフィールド対策も実装する
- 初期化時間はかなり長くてもいけるのでそこでまとめて計算する
- BFSして求めた全域木を順にたどるような感じで処理
- 最初のBFSがワーストケースで 180,000,000 [cycles] のうち 100,000,000 [cycles] くらいで済むのでまぁ行けそう
- 木の途中から再開できるようにしたりしてとりあえず256x256マップも妨害がなければ解けるようになる
4日目
- Scoreboardに提出してみる
- "Game over! Timed out. You scored 0 after 257109 game ticks."
- !?
- タイムアップまで一歩も動いていないらしい。さすがにやばいのでデバッグする。
- 大きいフィールド用AIと毎回探索するAIを結合する
- ついでにゴーストが近くにいるときも別のAIを用意する
- ゴーストとの距離がある程度あるときに同じところを行ったり来たりして煽ってるような挙動をする……
- 結局ゴーストAIが強すぎてずっと負けっぱなし……
- 時間内にできそうなレベルだと大した改善も思いつかなくなってきたので全域木の枝刈りを追加して提出。終了。
雑感
- 数時間でコンパイラ実装する人々やばい(しかも結構な比率で存在するらしい)
- 「こんなのできますか?」と聞いた1時間後に言語機能が追加されていたり
- ゴースト強すぎてかなりつらい
- きちんと履歴をとって膠着状態を回避すべきだった
- 割と最後の方までなぜか自分だけゴーストをロボットと呼んでしまっていた
SRM608 D1-Medium BigO
最後の詰めが甘い。
問題
頂点数nの有向グラフが与えられる。そのグラフ上で長さLのウォークの数が O(L^K) に漸近するとしたとき、Kとして取りうる最小の値を求めよ。
指数オーダなど O(L^K) では表せない場合は-1。
- n <= 50
SRM588 D1-Hard GameInDarknessDiv1
問題
2次元上のn*mセルからなる盤面にアリスとボブのトークンが1つずつ配置されている。盤面には壁があり、壁でないセルの隣接関係をグラフにすると木になっている。
アリスとボブが交互にトークンを隣接したセルに移動させる。もしアリスとボブのトークンが同じセルに入るとアリスの勝ち、100000手逃げ切ることができればボブの勝ちとなる。
ただし、アリスには双方の初期位置しかわからず、ボブの移動経路は知ることができない。また、ボブはアリスがゲーム中にどのようにトークンを移動させるかがすべてわかっている。
この条件のもとで、双方が最善な手を取った時にどちらが勝利するかを求めよ。
- 2 <= n, m <= 50